Compare commits
380 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a67c486385 | ||
|
|
8aa0c5a18e | ||
|
|
4c5eab8d62 | ||
|
|
5e016b38b0 | ||
|
|
5e4e135239 | ||
|
|
8c1d6c30de | ||
|
|
ec83ec0540 | ||
|
|
32dd0ae3ce | ||
|
|
e258ae8113 | ||
|
|
c11d26c887 | ||
|
|
56d9950898 | ||
|
|
60007bf43d | ||
|
|
1af15961e8 | ||
|
|
e6f82208d2 | ||
|
|
5cec9f1ef3 | ||
|
|
4cd27fc4a4 | ||
|
|
269a6160b7 | ||
|
|
3a077e4d1c | ||
|
|
4b0390efd5 | ||
|
|
41b52d3616 | ||
|
|
182daa3ae4 | ||
|
|
3f31bd349a | ||
|
|
ab25bd7d81 | ||
|
|
2c3ae8e6a8 | ||
|
|
4d0bad1533 | ||
|
|
e50c7f2211 | ||
|
|
128ee79abb | ||
|
|
6a6c6c275a | ||
|
|
04dddff75f | ||
|
|
b2cb898534 | ||
|
|
1268a72527 | ||
|
|
49e7c760a2 | ||
|
|
535602b67c | ||
|
|
8fee67c2e4 | ||
|
|
89a283440b | ||
|
|
774bff2b49 | ||
|
|
4d34a30dbf | ||
|
|
1320da0797 | ||
|
|
b171bdd7ea | ||
|
|
c1be6543ab | ||
|
|
473905239d | ||
|
|
20af2f4b8e | ||
|
|
e8c7809b1e | ||
|
|
26f96ee332 | ||
|
|
dc6d2d9c53 | ||
|
|
5e1925c7f1 | ||
|
|
bf57210edd | ||
|
|
2f7fed9df6 | ||
|
|
ce1b6a2228 | ||
|
|
9739898bd1 | ||
|
|
3c20fd688d | ||
|
|
e9ba3131db | ||
|
|
b93552ecfb | ||
|
|
7e52bb48b6 | ||
|
|
193d526583 | ||
|
|
c0100c1c3c | ||
|
|
0ffbed66fc | ||
|
|
8665c4a39e | ||
|
|
e4db812b82 | ||
|
|
90d02fa0ed | ||
|
|
940c15511c | ||
|
|
78a595ff4c | ||
|
|
8a2edbcc0a | ||
|
|
821a53f016 | ||
|
|
46c8d8dcee | ||
|
|
16d55baeb9 | ||
|
|
52eec32518 | ||
|
|
055893eb2b | ||
|
|
93302c34f7 | ||
|
|
1f7f486732 | ||
|
|
97ad3ab320 | ||
|
|
3abc2a0d2c | ||
|
|
9ec29a6f15 | ||
|
|
6d46bc1084 | ||
|
|
65708a5fc5 | ||
|
|
2588e7f1f8 | ||
|
|
21453982a3 | ||
|
|
8a8831ef84 | ||
|
|
c02b65a5e9 | ||
|
|
e5771f4c95 | ||
|
|
bf79e8c337 | ||
|
|
ba3a95fc3d | ||
|
|
35c0801f0d | ||
|
|
91e7252025 | ||
|
|
2cd9d9e6d3 | ||
|
|
7ae58475e5 | ||
|
|
87043fcaf2 | ||
|
|
3738563a73 | ||
|
|
859f8d107b | ||
|
|
c411f58c3d | ||
|
|
1e275345d4 | ||
|
|
c2cc1afac1 | ||
|
|
f5c87878f6 | ||
|
|
35021068c2 | ||
|
|
96c0dc0f87 | ||
|
|
1ca6abdd13 | ||
|
|
5049e7d09f | ||
|
|
911f00f8b2 | ||
|
|
fbfaa3db2e | ||
|
|
dc1e41a510 | ||
|
|
290ba7bbb4 | ||
|
|
fa9c5621e4 | ||
|
|
9d7bb0f232 | ||
|
|
722d766c28 | ||
|
|
cd26e12bdc | ||
|
|
45caec1460 | ||
|
|
9755caa71e | ||
|
|
5a16dbc77b | ||
|
|
3897ebbb55 | ||
|
|
e36dd6c968 | ||
|
|
5c4f213383 | ||
|
|
bf33dc60bd | ||
|
|
038caab680 | ||
|
|
344130ca7e | ||
|
|
1704e3df3d | ||
|
|
8ca73dd713 | ||
|
|
93a23c69bb | ||
|
|
cad39f36cc | ||
|
|
ece9324d32 | ||
|
|
3c7d9c86a9 | ||
|
|
cb27fc6309 | ||
|
|
b9cec5294e | ||
|
|
1b30d78b1a | ||
|
|
6771bd2d14 | ||
|
|
229990b50b | ||
|
|
de2081bec2 | ||
|
|
900032efa6 | ||
|
|
71e434517b | ||
|
|
7e4b454a42 | ||
|
|
6a91c08b55 | ||
|
|
2c2e738222 | ||
|
|
91bef5afc6 | ||
|
|
ec83261b43 | ||
|
|
a352a32bab | ||
|
|
25edae1563 | ||
|
|
8efe4d23de | ||
|
|
cc767ef1cb | ||
|
|
4eb2b9db7a | ||
|
|
54d9cba938 | ||
|
|
9bfb1c7a8e | ||
|
|
3566785030 | ||
|
|
e23fb21fd2 | ||
|
|
a5a1b8c9d7 | ||
|
|
99b164e240 | ||
|
|
6fff8ff642 | ||
|
|
d0490d318a | ||
|
|
35823fb11c | ||
|
|
b42285f838 | ||
|
|
d65843fb6d | ||
|
|
4cf20c4ede | ||
|
|
d0272cc3fb | ||
|
|
29f127e226 | ||
|
|
0d820ebb2c | ||
|
|
006f4390a7 | ||
|
|
05bebbe67b | ||
|
|
354ef42fcb | ||
|
|
bace7f8b91 | ||
|
|
97644e2479 | ||
|
|
19f3088e62 | ||
|
|
3a1ca78182 | ||
|
|
0a1d3dbf76 | ||
|
|
102a93361c | ||
|
|
27ea77eac7 | ||
|
|
e19c50fe3d | ||
|
|
18dedd1c77 | ||
|
|
edc83a77fd | ||
|
|
41b91145d6 | ||
|
|
c77e84bbe6 | ||
|
|
beb8e821ae | ||
|
|
3011b7980e | ||
|
|
8779286066 | ||
|
|
c5e36251a4 | ||
|
|
d68b43d328 | ||
|
|
7f2275a11a | ||
|
|
51621715f9 | ||
|
|
341efc62d2 | ||
|
|
afa1f918ee | ||
|
|
82f5dafb5d | ||
|
|
2a3ed44fd5 | ||
|
|
922d413cd6 | ||
|
|
0134b0d792 | ||
|
|
ee0cbf4c10 | ||
|
|
6b1a59224f | ||
|
|
29d926746e | ||
|
|
120c77ade6 | ||
|
|
7f1d98ecc8 | ||
|
|
e7c08331e1 | ||
|
|
b29f0fd912 | ||
|
|
a36b7c4cb4 | ||
|
|
a815ea0319 | ||
|
|
4cfd5383d9 | ||
|
|
d3458164d6 | ||
|
|
72ed94447a | ||
|
|
fbd601213f | ||
|
|
becb6295a6 | ||
|
|
5c40e50351 | ||
|
|
99035900ff | ||
|
|
76512e3fc6 | ||
|
|
c54ef94e03 | ||
|
|
ac24a6a09e | ||
|
|
2b80bac9f1 | ||
|
|
19f280c294 | ||
|
|
dc203ec04b | ||
|
|
c8fa914ec7 | ||
|
|
8cf33c16f4 | ||
|
|
6d09df1de7 | ||
|
|
a7eedf25db | ||
|
|
bc2d44c6bb | ||
|
|
3eee4c1db4 | ||
|
|
9e31e52794 | ||
|
|
49791c3ea3 | ||
|
|
c421c2a78f | ||
|
|
d90e8e3d29 | ||
|
|
cc792c93e4 | ||
|
|
4ef5fe8f69 | ||
|
|
0dd2a93b21 | ||
|
|
1c7052e5c2 | ||
|
|
e7fb835d89 | ||
|
|
0a4b452870 | ||
|
|
7cc84de9c0 | ||
|
|
7ac16901e2 | ||
|
|
1ce6d1f0f8 | ||
|
|
cb0d241ef1 | ||
|
|
e43901c75d | ||
|
|
c08fdd3a69 | ||
|
|
c00d610581 | ||
|
|
55a00fdf52 | ||
|
|
1295dc604b | ||
|
|
0fe28c1133 | ||
|
|
b35027525e | ||
|
|
906949476d | ||
|
|
f7fee75206 | ||
|
|
101bc5b043 | ||
|
|
7e222f77b4 | ||
|
|
e89a33df92 | ||
|
|
1523dcf803 | ||
|
|
4077af5fca | ||
|
|
3e9c76d4f9 | ||
|
|
643832c252 | ||
|
|
54d5efba3e | ||
|
|
5480c0346d | ||
|
|
4c3db801d2 | ||
|
|
17a131648b | ||
|
|
814b232746 | ||
|
|
c4a7764808 | ||
|
|
4af56eb389 | ||
|
|
098fb29f4d | ||
|
|
e028a49441 | ||
|
|
9a367a2266 | ||
|
|
0254645eb5 | ||
|
|
bad5cc9a80 | ||
|
|
87fdf9d178 | ||
|
|
89122ecfad | ||
|
|
ad1cc403d2 | ||
|
|
ef46c9bd6f | ||
|
|
081aa8ce16 | ||
|
|
7607a7c315 | ||
|
|
4d92f5fc50 | ||
|
|
08f5601449 | ||
|
|
18f1f69000 | ||
|
|
511afe1c22 | ||
|
|
25f732c72a | ||
|
|
fc944cc77d | ||
|
|
47fbd86278 | ||
|
|
b656935ce6 | ||
|
|
49684b5eac | ||
|
|
3b6689f2a3 | ||
|
|
613e9c4e57 | ||
|
|
cda5970488 | ||
|
|
79669bfee1 | ||
|
|
6cddce7ada | ||
|
|
dab9bb1d8d | ||
|
|
a25e380476 | ||
|
|
2b7b594428 | ||
|
|
6e9a1f5e29 | ||
|
|
5d3a391e3b | ||
|
|
3395bcfc0a | ||
|
|
e22621e3bb | ||
|
|
878a1d4f47 | ||
|
|
994a4deb26 | ||
|
|
b1f93f66ca | ||
|
|
f1387a951a | ||
|
|
07c67974be | ||
|
|
17ca9b007d | ||
|
|
c7f516725a | ||
|
|
11a7e2f367 | ||
|
|
67e055497f | ||
|
|
528d106bad | ||
|
|
45bd7301df | ||
|
|
4abb9f9c3e | ||
|
|
4ce502e1ad | ||
|
|
d08aabd1e9 | ||
|
|
7a9e3f18d5 | ||
|
|
161e511f31 | ||
|
|
56a9ece57a | ||
|
|
e5321ee030 | ||
|
|
10dcb80f6e | ||
|
|
c72d2c417c | ||
|
|
e130fa1954 | ||
|
|
02e0c98530 | ||
|
|
681a96b873 | ||
|
|
98a6509483 | ||
|
|
bbe79331e9 | ||
|
|
9700ff7f49 | ||
|
|
718f7d906e | ||
|
|
b65ae67040 | ||
|
|
c5994c620e | ||
|
|
0d3d32f574 | ||
|
|
754a2316b7 | ||
|
|
939e277c2a | ||
|
|
055e57f476 | ||
|
|
4da0131313 | ||
|
|
fa33b58155 | ||
|
|
4f2305fcd9 | ||
|
|
de67f8d936 | ||
|
|
3d0214e7c2 | ||
|
|
cb425c8da4 | ||
|
|
89129f3ae8 | ||
|
|
f82a21e9a2 | ||
|
|
206ca83bf0 | ||
|
|
41b3690737 | ||
|
|
4dfc83d374 | ||
|
|
33ddaa8c0e | ||
|
|
71aba0a6e1 | ||
|
|
5b47f0da6c | ||
|
|
dcf5fd577b | ||
|
|
de3dc5f901 | ||
|
|
8746a9abd1 | ||
|
|
bdb56539d4 | ||
|
|
6fa4cd9689 | ||
|
|
4e2ffb150b | ||
|
|
37c896a869 | ||
|
|
f478ea5a77 | ||
|
|
1c35ce4d47 | ||
|
|
eb7c42c8dd | ||
|
|
9480088d74 | ||
|
|
0db9c6797b | ||
|
|
9551b92273 | ||
|
|
9bd3869001 | ||
|
|
5819634f1f | ||
|
|
59e2e347ed | ||
|
|
5aa80924c3 | ||
|
|
e02f07929b | ||
|
|
4fd86d8f4b | ||
|
|
e2551f9400 | ||
|
|
e44e7b6231 | ||
|
|
bb77c4f740 | ||
|
|
c6402b6939 | ||
|
|
134bdbc166 | ||
|
|
128a68f52f | ||
|
|
6fca91e7f3 | ||
|
|
f2db849f00 | ||
|
|
7b8b1ca150 | ||
|
|
41a6e83b32 | ||
|
|
7e88ffe4af | ||
|
|
52cab96832 | ||
|
|
671f38c51c | ||
|
|
ff83a253be | ||
|
|
0341b9a79d | ||
|
|
dc808dec66 | ||
|
|
2397e6371a | ||
|
|
a23c27b57b | ||
|
|
9a55916521 | ||
|
|
384d891b65 | ||
|
|
11f75f24fa | ||
|
|
5565cbc475 | ||
|
|
c84be824fd | ||
|
|
46108e81c4 | ||
|
|
b5f79bc0b9 | ||
|
|
30810cc293 | ||
|
|
df7ce49a25 | ||
|
|
67e13d7505 | ||
|
|
8a82fb392d | ||
|
|
15d9c26352 | ||
|
|
9c8d4f24e1 | ||
|
|
cd2890a4cf | ||
|
|
b1b7c813b2 | ||
|
|
61222eb0fa | ||
|
|
40c577fa31 | ||
|
|
e024acb1a9 |
16
.ci/azure-pipelines.yml
Normal file
16
.ci/azure-pipelines.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
trigger:
|
||||
- master
|
||||
- release
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
inputs:
|
||||
gradleWrapperFile: 'gradlew'
|
||||
tasks: 'build'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
javaHomeOption: 'JDKVersion'
|
||||
sonarQubeRunAnalysis: false
|
||||
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[{*.kts, *.kt}]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
@@ -6,6 +6,9 @@
|
||||
- [grafixeyehero](https://github.com/grafixeyehero)
|
||||
- [BnMcG](https://github.com/bnmcg)
|
||||
- [Letroll](https://github.com/letroll)
|
||||
- [AndreasGB](https://github.com/andreasgb)
|
||||
- [joern-h](https://github.com/joern-h)
|
||||
- [Niels van Velzen](https://github.com/nielsvanvelzen)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
||||
46
README.md
46
README.md
@@ -1,6 +1,50 @@
|
||||
<h1 align="center">Jellyfin Android TV</h1>
|
||||
<h3 align="center">Part of the <a href="https://jellyfin.media">Jellyfin Project</a></h3>
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
Jellyfin Android TV is a Jellyfin client for Android TV, nVidia Shield, and Amazon Fire TV devices.
|
||||
<img alt="Logo banner" src="https://raw.githubusercontent.com/jellyfin/jellyfin-ux/master/branding/SVG/banner-logo-solid.svg?sanitize=true"/>
|
||||
<br/><br/>
|
||||
<a href="https://github.com/jellyfin/jellyfin-androidtv">
|
||||
<img alt="GPL 2.0 License" src="https://img.shields.io/github/license/jellyfin/jellyfin-androidtv.svg"/>
|
||||
</a>
|
||||
<a href="https://github.com/jellyfin/jellyfin-androidtv/releases">
|
||||
<img alt="Current Release" src="https://img.shields.io/github/release/jellyfin/jellyfin-androidtv.svg"/>
|
||||
</a>
|
||||
<a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-androidtv/?utm_source=widget">
|
||||
<img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-androidtv/svg-badge.svg" alt="Translation status" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://opencollective.com/jellyfin">
|
||||
<img alt="Donate" src="https://img.shields.io/opencollective/all/jellyfin.svg?label=backers"/>
|
||||
</a>
|
||||
<a href="https://features.jellyfin.org">
|
||||
<img alt="Feature Requests" src="https://img.shields.io/badge/fider-vote%20on%20features-success.svg"/>
|
||||
</a>
|
||||
<a href="https://forum.jellyfin.org">
|
||||
<img alt="Discuss on our Forum" src="https://img.shields.io/discourse/https/forum.jellyfin.org/users.svg"/>
|
||||
</a>
|
||||
<a href="https://matrix.to/#/+jellyfin:matrix.org">
|
||||
<img alt="Chat on Matrix" src="https://img.shields.io/matrix/jellyfin:matrix.org.svg?logo=matrix"/>
|
||||
</a>
|
||||
<a href="https://www.reddit.com/r/jellyfin">
|
||||
<img alt="Join our Subreddit" src="https://img.shields.io/badge/reddit-r%2Fjellyfin-%23FF5700.svg"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://play.google.com/store/apps/details?id=org.jellyfin.androidtv">
|
||||
<img width="200" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Get it on Google Play"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://www.amazon.com/gp/aw/d/B07TX7Z725">
|
||||
<img width="170" src="https://images-na.ssl-images-amazon.com/images/G/01/mobile-apps/devportal2/res/images/amazon-appstore-badge-english-black.png" alt="Available at Amazon Appstore"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://repo.jellyfin.org/releases/client/androidtv/">Download archive</a>
|
||||
</p>
|
||||
|
||||
Jellyfin Android TV is a Jellyfin client for Android TV, nVidia Shield, and Amazon Fire TV devices. We welcome all contributions and pull requests! If you have a larger feature in mind please open an issue so we can discuss the implementation before you start. Translations can be improved very easily from our <a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-androidtv">Weblate</a> instance. Look through the following graphic to see if your native language could use some work!
|
||||
|
||||
<a href="https://translate.jellyfin.org/engage/jellyfin/?utm_source=widget">
|
||||
<img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-androidtv/multi-auto.svg" alt="Detailed Translation Status"/>
|
||||
</a>
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
apply plugin: 'com.android.application'
|
||||
def version = "0.9.0"
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
def version = "0.11.1"
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion "28.0.3"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility 1.8
|
||||
targetCompatibility 1.8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 900
|
||||
versionCode 906
|
||||
versionName version
|
||||
}
|
||||
|
||||
@@ -22,33 +29,43 @@ android {
|
||||
debuggable true
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "version"
|
||||
productFlavors {
|
||||
google {
|
||||
dimension = "version"
|
||||
versionName = version + "g"
|
||||
}
|
||||
amazon {
|
||||
dimension = "version"
|
||||
versionName = version + "a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def acraVersion = '5.4.0'
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
amazonImplementation 'com.github.jellyfin:exomedia:amazon~2.5.6-SNAPSHOT'
|
||||
googleImplementation 'com.devbrackets.android:exomedia:2.5.6'
|
||||
implementation 'com.github.jellyfin:jellyfin-apiclient-java:master-SNAPSHOT'
|
||||
implementation 'com.android.support:recyclerview-v7:28.0.0'
|
||||
implementation 'com.android.support:leanback-v17:28.0.0'
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.android.support:palette-v7:28.0.0'
|
||||
|
||||
// This is split like this because this is a modular gradle project on jitpack
|
||||
// see https://github.com/jitpack/gradle-modular
|
||||
// The split allows dependency substitution to work in the settings.gradle file,
|
||||
// at least for the 'library' part. 'android' part is still broken somehow, but this
|
||||
// is better than nothing.
|
||||
implementation 'com.github.jellyfin.jellyfin-apiclient-java:android:0.5.0'
|
||||
implementation 'com.github.jellyfin.jellyfin-apiclient-java:library:0.5.0'
|
||||
implementation 'com.amazon.android:exoplayer:2.10.6'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation 'androidx.leanback:leanback:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.3.2'
|
||||
implementation 'com.github.bumptech.glide:glide:3.7.0'
|
||||
implementation 'com.squareup.okhttp:okhttp:2.7.0'
|
||||
implementation 'com.squareup.okhttp:okhttp-urlconnection:2.7.0'
|
||||
implementation 'com.google.guava:guava:18.0'
|
||||
implementation 'com.flaviofaria:kenburnsview:1.0.6'
|
||||
implementation 'org.videolan.android:libvlc-all:3.1.12'
|
||||
implementation "ch.acra:acra-http:$acraVersion"
|
||||
implementation "ch.acra:acra-dialog:$acraVersion"
|
||||
implementation "ch.acra:acra-limiter:$acraVersion"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
implementation "androidx.core:core-ktx:1.1.0"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.tvprovider:tvprovider:1.0.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
f5887942aa8e8f94380f218e5e0cd9ddf3974485
|
||||
@@ -11,6 +11,9 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
<!-- Needed for ChannelManager on older devices (< API 23) -->
|
||||
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
@@ -20,22 +23,23 @@
|
||||
|
||||
<application
|
||||
android:name=".TvApp"
|
||||
android:banner="@drawable/app_icon"
|
||||
android:icon="@drawable/logoicon512"
|
||||
android:banner="@drawable/app_logo"
|
||||
android:icon="@drawable/app_icon_large"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:theme="@style/Theme.Jellyfin"
|
||||
android:allowBackup="false"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity
|
||||
android:name=".browsing.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:logo="@drawable/app_icon"
|
||||
android:logo="@drawable/app_logo"
|
||||
android:screenOrientation="landscape" />
|
||||
<activity
|
||||
android:name=".startup.StartupActivity"
|
||||
android:label="@string/app_name"
|
||||
android:logo="@drawable/app_icon"
|
||||
android:logo="@drawable/app_logo"
|
||||
android:noHistory="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -80,13 +84,6 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:authorities="org.jellyfin.androidtv.recommendations"
|
||||
android:name=".integration.RecommendationContentProvider"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
|
||||
<activity
|
||||
android:name=".details.ExpandedTextActivity"
|
||||
android:label="@string/title_activity_expanded_text" />
|
||||
|
||||
@@ -1,59 +1,71 @@
|
||||
package org.jellyfin.androidtv;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.graphics.Palette;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.acra.annotation.AcraCore;
|
||||
import org.acra.annotation.AcraDialog;
|
||||
import org.acra.annotation.AcraHttpSender;
|
||||
import org.acra.annotation.AcraLimiter;
|
||||
import org.acra.sender.HttpSender;
|
||||
import org.jellyfin.androidtv.base.BaseActivity;
|
||||
import org.jellyfin.androidtv.livetv.TvManager;
|
||||
import org.jellyfin.androidtv.model.DisplayPriorityType;
|
||||
import org.jellyfin.androidtv.model.LogonCredentials;
|
||||
import org.jellyfin.androidtv.playback.ExternalPlayerActivity;
|
||||
import org.jellyfin.androidtv.playback.MediaManager;
|
||||
import org.jellyfin.androidtv.playback.PlaybackController;
|
||||
import org.jellyfin.androidtv.playback.PlaybackManager;
|
||||
import org.jellyfin.androidtv.playback.PlaybackOverlayActivity;
|
||||
import org.jellyfin.androidtv.search.SearchActivity;
|
||||
import org.jellyfin.androidtv.startup.LogonCredentials;
|
||||
import org.jellyfin.androidtv.util.DeviceUtils;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.ApiClient;
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.IConnectionManager;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.interaction.GsonJsonSerializer;
|
||||
import org.jellyfin.apiclient.interaction.VolleyHttpClient;
|
||||
import org.jellyfin.apiclient.logging.AndroidLogger;
|
||||
import org.jellyfin.apiclient.model.configuration.ServerConfiguration;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
import org.jellyfin.apiclient.model.entities.DisplayPreferences;
|
||||
import org.jellyfin.apiclient.model.logging.ILogger;
|
||||
import org.jellyfin.apiclient.model.net.EndPointInfo;
|
||||
import org.jellyfin.apiclient.model.system.SystemInfo;
|
||||
import org.jellyfin.apiclient.model.serialization.GsonJsonSerializer;
|
||||
|
||||
public class TvApp extends Application implements ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
@AcraCore(buildConfigClass = BuildConfig.class)
|
||||
@AcraHttpSender(
|
||||
uri = "https://collector.tracepot.com/a2eda9d9",
|
||||
httpMethod = HttpSender.Method.POST
|
||||
)
|
||||
@AcraDialog(
|
||||
resTitle = R.string.acra_dialog_title,
|
||||
resText = R.string.acra_dialog_text,
|
||||
resTheme = R.style.Theme_Jellyfin
|
||||
)
|
||||
@AcraLimiter
|
||||
public class TvApp extends Application {
|
||||
// The minimum supported server version. Trying to connect to an older server will display an error.
|
||||
public static final String MINIMUM_SERVER_VERSION = "10.3.0";
|
||||
public static final String CREDENTIALS_PATH = "org.jellyfin.androidtv.login.json";
|
||||
|
||||
public static final int LIVE_TV_GUIDE_OPTION_ID = 1000;
|
||||
public static final int LIVE_TV_RECORDINGS_OPTION_ID = 2000;
|
||||
public static final int VIDEO_QUEUE_OPTION_ID = 3000;
|
||||
@@ -61,7 +73,6 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
public static final int LIVE_TV_SERIES_OPTION_ID = 5000;
|
||||
|
||||
private static final String TAG = "Jellyfin-AndroidTV";
|
||||
private static final int SEARCH_PERMISSION = 0;
|
||||
|
||||
private ILogger logger;
|
||||
private IConnectionManager connectionManager;
|
||||
@@ -69,7 +80,6 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
private GsonJsonSerializer serializer;
|
||||
private static TvApp app;
|
||||
private UserDto currentUser;
|
||||
private SystemInfo currentSystemInfo;
|
||||
private BaseItemDto currentPlayingItem;
|
||||
private BaseItemDto lastPlayedItem;
|
||||
private PlaybackController playbackController;
|
||||
@@ -98,8 +108,6 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
private long lastMusicPlayback = System.currentTimeMillis();
|
||||
private long lastUserInteraction = System.currentTimeMillis();
|
||||
|
||||
private boolean searchAllowed = Build.VERSION.SDK_INT < 23;
|
||||
|
||||
private GradientDrawable currentBackgroundGradient;
|
||||
|
||||
private boolean audioMuted;
|
||||
@@ -110,6 +118,13 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
|
||||
private LogonCredentials configuredAutoCredentials;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
|
||||
ACRA.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@@ -120,17 +135,6 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
setCurrentBackgroundGradient(new int[] {ContextCompat.getColor(this, R.color.lb_default_brand_color_dark), ContextCompat.getColor(this, R.color.lb_default_brand_color)});
|
||||
|
||||
logger.Info("Application object created");
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
logger.FatalException("Uncaught Exception", new Exception(ex));
|
||||
ex.printStackTrace();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(10);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static TvApp getApplication() {
|
||||
@@ -222,120 +226,13 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
this.playbackController = playbackController;
|
||||
}
|
||||
|
||||
public SystemInfo getCurrentSystemInfo() { return currentSystemInfo; }
|
||||
|
||||
public void loadSystemInfo() {
|
||||
if (getApiClient() != null) {
|
||||
getApiClient().GetSystemInfoAsync(new Response<SystemInfo>() {
|
||||
@Override
|
||||
public void onResponse(SystemInfo response) {
|
||||
currentSystemInfo = response;
|
||||
logger.Info("Current server is " + response.getServerName() + " (ver " + response.getVersion() + ") running on " + response.getOperatingSystemDisplayName());
|
||||
//Server compat warning
|
||||
if (getCurrentActivity() != null && !Utils.versionGreaterThanOrEqual(currentSystemInfo.getVersion(), "3.0.5882.0")) {
|
||||
new AlertDialog.Builder(getCurrentActivity())
|
||||
.setTitle("Incompatible Server Version")
|
||||
.setMessage("Please update your Jellyfin Server to avoid potential seeking problems during playback.")
|
||||
.setPositiveButton(getString(R.string.btn_ok), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
}
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
logger.ErrorException("Unable to obtain system info.", exception);
|
||||
}
|
||||
});
|
||||
|
||||
//Also get server configuration and fill in max remote bitrate if we are remote
|
||||
getApiClient().GetEndPointInfo(new Response<EndPointInfo>() {
|
||||
@Override
|
||||
public void onResponse(EndPointInfo response) {
|
||||
if (!response.getIsInNetwork()) {
|
||||
getApiClient().GetServerConfigurationAsync(new Response<ServerConfiguration>() {
|
||||
@Override
|
||||
public void onResponse(ServerConfiguration response) {
|
||||
serverConfiguration = response;
|
||||
maxRemoteBitrate = serverConfiguration.getRemoteClientBitrateLimit();
|
||||
getLogger().Info("Server bitrate limit set to ", maxRemoteBitrate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
getLogger().ErrorException("Unable to retrieve server configuration",exception);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
getLogger().Info("** Local connection - no server bitrate limit");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void showSearch(final Activity activity, boolean musicOnly) {
|
||||
if (!searchAllowed && ContextCompat.checkSelfPermission(activity,
|
||||
Manifest.permission.RECORD_AUDIO)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
//request necessary permission
|
||||
logger.Info("Requesting search permission...");
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.RECORD_AUDIO)) {
|
||||
//show explanation
|
||||
logger.Info("Show rationale for permission");
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle("Search Permission")
|
||||
.setMessage("Search requires permission to record audio in order to use the microphone for voice search")
|
||||
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
ActivityCompat.requestPermissions(activity, new String[] {Manifest.permission.RECORD_AUDIO}, SEARCH_PERMISSION);
|
||||
}
|
||||
}).show();
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(activity, new String[] {Manifest.permission.RECORD_AUDIO}, SEARCH_PERMISSION);
|
||||
}
|
||||
} else {
|
||||
showSearchInternal(activity, musicOnly);
|
||||
}
|
||||
}
|
||||
|
||||
private void showSearchInternal(Context activity, boolean musicOnly) {
|
||||
Intent intent = new Intent(activity, SearchActivity.class);
|
||||
if (musicOnly) intent.putExtra("MusicOnly", true);
|
||||
intent.putExtra("MusicOnly", musicOnly);
|
||||
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case SEARCH_PERMISSION: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
// permission was granted, yay!
|
||||
searchAllowed = true;
|
||||
showSearchInternal(getCurrentActivity(), false);
|
||||
} else {
|
||||
|
||||
// permission denied, boo! Disable the
|
||||
// functionality that depends on this permission.
|
||||
Utils.showToast(this, "Search not allowed");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// other 'case' lines to check for other
|
||||
// permissions this app might request
|
||||
}
|
||||
}
|
||||
|
||||
public void showMessage(String title, String msg) {
|
||||
if (currentActivity != null) {
|
||||
currentActivity.showMessage(title, msg);
|
||||
@@ -372,23 +269,23 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
return getPrefs().getString("pref_login_behavior", "0").equals("1") && getConfiguredAutoCredentials().getServerInfo().getId() != null;
|
||||
}
|
||||
|
||||
public boolean useExternalPlayer(String itemType) {
|
||||
public boolean useExternalPlayer(BaseItemType itemType) {
|
||||
switch (itemType) {
|
||||
case "Movie":
|
||||
case "Episode":
|
||||
case "Video":
|
||||
case "Series":
|
||||
case "Recording":
|
||||
return getPrefs().getBoolean("pref_video_use_external", false);
|
||||
case "TvChannel":
|
||||
case "Program":
|
||||
case Movie:
|
||||
case Episode:
|
||||
case Video:
|
||||
case Series:
|
||||
case Recording:
|
||||
return getPrefs().getString("pref_video_player", "auto").equals("external");
|
||||
case TvChannel:
|
||||
case Program:
|
||||
return getPrefs().getBoolean("pref_live_tv_use_external", false);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Class getPlaybackActivityClass(String itemType) {
|
||||
public Class getPlaybackActivityClass(BaseItemType itemType) {
|
||||
return useExternalPlayer(itemType) ? ExternalPlayerActivity.class : PlaybackOverlayActivity.class;
|
||||
}
|
||||
|
||||
@@ -623,14 +520,6 @@ public class TvApp extends Application implements ActivityCompat.OnRequestPermis
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
public boolean isSearchAllowed() {
|
||||
return searchAllowed;
|
||||
}
|
||||
|
||||
public void setSearchAllowed(boolean searchAllowed) {
|
||||
this.searchAllowed = searchAllowed;
|
||||
}
|
||||
|
||||
public BaseItemDto getLastPlayedItem() {
|
||||
return lastPlayedItem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.jellyfin.androidtv.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -17,8 +16,10 @@ import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
public class BaseActivity extends Activity {
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
public abstract class BaseActivity extends FragmentActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
private TvApp app = TvApp.getApplication();
|
||||
private long timeoutInterval = 3600000;
|
||||
private Handler handler = new Handler();
|
||||
@@ -38,7 +39,7 @@ public class BaseActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
timeoutInterval = Long.parseLong(PreferenceManager.getDefaultSharedPreferences(app).getString("pref_auto_logoff_timeout","3600000"));
|
||||
timeoutInterval = Long.parseLong(PreferenceManager.getDefaultSharedPreferences(app).getString("pref_auto_logoff_timeout", "3600000"));
|
||||
startAutoLogoffLoop();
|
||||
TvApp.getApplication().setCurrentActivity(this);
|
||||
|
||||
@@ -79,8 +80,12 @@ public class BaseActivity extends Activity {
|
||||
if (messageUi != null && !isFinishing()) {
|
||||
messageTitle.setText(title);
|
||||
messageMessage.setText(msg);
|
||||
if (icon != null) messageIcon.setImageResource(icon);
|
||||
if (background != null) messageUi.setBackgroundResource(background);
|
||||
if (icon != null) {
|
||||
messageIcon.setImageResource(icon);
|
||||
}
|
||||
if (background != null) {
|
||||
messageUi.setBackgroundResource(background);
|
||||
}
|
||||
messageUi.animate().x(msgPos).alpha(1).setDuration(300);
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
@@ -89,7 +94,7 @@ public class BaseActivity extends Activity {
|
||||
messageUi.animate().alpha(0).x(farRight).setDuration(300);
|
||||
}
|
||||
}
|
||||
},timeout);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -122,7 +127,9 @@ public class BaseActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (handler != null && loop != null) handler.removeCallbacks(loop);
|
||||
if (handler != null && loop != null) {
|
||||
handler.removeCallbacks(loop);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -133,7 +140,9 @@ public class BaseActivity extends Activity {
|
||||
}
|
||||
|
||||
private void startAutoLogoffLoop() {
|
||||
if (loop != null) return;
|
||||
if (loop != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
loop = new Runnable() {
|
||||
@Override
|
||||
@@ -165,7 +174,9 @@ public class BaseActivity extends Activity {
|
||||
}
|
||||
|
||||
public void sendMessage(CustomMessage message) {
|
||||
if (messageListener != null) messageListener.onMessageReceived(message);
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,13 +2,13 @@ package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
@@ -66,13 +66,12 @@ public class BrowseFolderFragment extends StdBrowseFragment {
|
||||
|
||||
GridButtonPresenter mGridPresenter = new GridButtonPresenter();
|
||||
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
|
||||
gridRowAdapter.add(new GridButton(BY_LETTER, mApplication.getString(R.string.lbl_by_letter), R.drawable.byletter));
|
||||
gridRowAdapter.add(new GridButton(BY_LETTER, mApplication.getString(R.string.lbl_by_letter), R.drawable.tile_letters));
|
||||
if (itemTypeString != null && itemTypeString.equals("Movie"))
|
||||
gridRowAdapter.add(new GridButton(SUGGESTED, mApplication.getString(R.string.lbl_suggested), R.drawable.suggestions));
|
||||
gridRowAdapter.add(new GridButton(GENRES, mApplication.getString(R.string.lbl_genres), R.drawable.genres));
|
||||
gridRowAdapter.add(new GridButton(PERSONS, mApplication.getString(R.string.lbl_performers), R.drawable.actors));
|
||||
gridRowAdapter.add(new GridButton(SUGGESTED, mApplication.getString(R.string.lbl_suggested), R.drawable.tile_suggestions));
|
||||
gridRowAdapter.add(new GridButton(GENRES, mApplication.getString(R.string.lbl_genres), R.drawable.tile_genres));
|
||||
gridRowAdapter.add(new GridButton(PERSONS, mApplication.getString(R.string.lbl_performers), R.drawable.tile_actors));
|
||||
rowAdapter.add(new ListRow(gridHeader, gridRowAdapter));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +85,6 @@ public class BrowseFolderFragment extends StdBrowseFragment {
|
||||
@Override
|
||||
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
|
||||
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||
|
||||
if (item instanceof GridButton) {
|
||||
switch (((GridButton) item).getId()) {
|
||||
case BY_LETTER:
|
||||
@@ -96,7 +94,6 @@ public class BrowseFolderFragment extends StdBrowseFragment {
|
||||
|
||||
getActivity().startActivity(intent);
|
||||
break;
|
||||
|
||||
case GENRES:
|
||||
Intent genreIntent = new Intent(getActivity(), ByGenreActivity.class);
|
||||
genreIntent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(mFolder));
|
||||
@@ -104,7 +101,6 @@ public class BrowseFolderFragment extends StdBrowseFragment {
|
||||
|
||||
getActivity().startActivity(genreIntent);
|
||||
break;
|
||||
|
||||
case SUGGESTED:
|
||||
Intent suggIntent = new Intent(getActivity(), SuggestedMoviesActivity.class);
|
||||
suggIntent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(mFolder));
|
||||
@@ -112,7 +108,6 @@ public class BrowseFolderFragment extends StdBrowseFragment {
|
||||
|
||||
getActivity().startActivity(suggIntent);
|
||||
break;
|
||||
|
||||
case PERSONS:
|
||||
Intent personIntent = new Intent(getActivity(), BrowsePersonsActivity.class);
|
||||
personIntent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(mFolder));
|
||||
@@ -120,10 +115,8 @@ public class BrowseFolderFragment extends StdBrowseFragment {
|
||||
|
||||
getActivity().startActivity(personIntent);
|
||||
break;
|
||||
|
||||
default:
|
||||
Toast.makeText(getActivity(), item.toString() + mApplication.getString(R.string.msg_not_implemented), Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
Toast.makeText(getActivity(), item.toString() + mApplication.getString(R.string.msg_not_implemented), Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.model.ChangeTriggerType;
|
||||
import org.jellyfin.androidtv.querying.StdItemQuery;
|
||||
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.querying.ArtistsQuery;
|
||||
import org.jellyfin.apiclient.model.querying.ItemFields;
|
||||
|
||||
@@ -30,7 +31,7 @@ public class BrowseGridFragment extends StdGridFragment {
|
||||
protected void setupQueries(IGridLoader gridLoader) {
|
||||
StdItemQuery query = new StdItemQuery(new ItemFields[] {ItemFields.PrimaryImageAspectRatio});
|
||||
query.setParentId(mParentId);
|
||||
if (mFolder.getType().equals("UserView") || mFolder.getType().equals("CollectionFolder")) {
|
||||
if (mFolder.getBaseItemType() == BaseItemType.UserView || mFolder.getBaseItemType() == BaseItemType.CollectionFolder) {
|
||||
String type = mFolder.getCollectionType() != null ? mFolder.getCollectionType().toLowerCase() : "";
|
||||
switch (type) {
|
||||
case "movies":
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
@@ -19,6 +19,7 @@ import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.entities.LocationType;
|
||||
import org.jellyfin.apiclient.model.livetv.RecordingGroupQuery;
|
||||
import org.jellyfin.apiclient.model.livetv.RecordingQuery;
|
||||
@@ -119,7 +120,7 @@ public class BrowseRecordingsFragment extends EnhancedBrowseFragment {
|
||||
programInfo.setChannelName(timer.getChannelName());
|
||||
programInfo.setName(Utils.getSafeValue(timer.getName(), "Unknown"));
|
||||
TvApp.getApplication().getLogger().Warn("No program info for timer %s. Creating one...", programInfo.getName());
|
||||
programInfo.setType("Program");
|
||||
programInfo.setBaseItemType(BaseItemType.Program);
|
||||
programInfo.setTimerId(timer.getId());
|
||||
programInfo.setSeriesTimerId(timer.getSeriesTimerId());
|
||||
programInfo.setStartDate(timer.getStartDate());
|
||||
@@ -158,8 +159,8 @@ public class BrowseRecordingsFragment extends EnhancedBrowseFragment {
|
||||
|
||||
GridButtonPresenter mGridPresenter = new GridButtonPresenter();
|
||||
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
|
||||
gridRowAdapter.add(new GridButton(SCHEDULE, TvApp.getApplication().getString(R.string.lbl_schedule), R.drawable.clock));
|
||||
gridRowAdapter.add(new GridButton(SERIES, mActivity.getString(R.string.lbl_series_recordings), R.drawable.seriestimerp));
|
||||
gridRowAdapter.add(new GridButton(SCHEDULE, TvApp.getApplication().getString(R.string.lbl_schedule), R.drawable.tile_port_time));
|
||||
gridRowAdapter.add(new GridButton(SERIES, mActivity.getString(R.string.lbl_series_recordings), R.drawable.tile_port_series_timer));
|
||||
rowAdapter.add(new ListRow(gridHeader, gridRowAdapter));
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
@@ -21,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.entities.LocationType;
|
||||
import org.jellyfin.apiclient.model.entities.SortOrder;
|
||||
import org.jellyfin.apiclient.model.livetv.LiveTvChannelQuery;
|
||||
@@ -39,29 +39,13 @@ import org.jellyfin.apiclient.model.querying.LatestItemsQuery;
|
||||
import org.jellyfin.apiclient.model.querying.NextUpQuery;
|
||||
import org.jellyfin.apiclient.model.results.TimerInfoDtoResult;
|
||||
|
||||
/**
|
||||
* Created by Eric on 12/4/2014.
|
||||
*/
|
||||
public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
|
||||
boolean isLiveTvLibrary;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
}
|
||||
private boolean isLiveTvLibrary;
|
||||
|
||||
@Override
|
||||
protected void setupQueries(final IRowLoader rowLoader) {
|
||||
String type = mFolder.getCollectionType() != null ? mFolder.getCollectionType().toLowerCase() : "";
|
||||
switch (type)
|
||||
{
|
||||
switch (type) {
|
||||
case "movies":
|
||||
itemTypeString = "Movie";
|
||||
|
||||
@@ -77,15 +61,15 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
resumeMovies.setFilters(new ItemFilter[]{ItemFilter.IsResumable});
|
||||
resumeMovies.setSortBy(new String[]{ItemSortBy.DatePlayed});
|
||||
resumeMovies.setSortOrder(SortOrder.Descending);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_continue_watching), resumeMovies, 0, new ChangeTriggerType[] {ChangeTriggerType.MoviePlayback}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_continue_watching), resumeMovies, 0, new ChangeTriggerType[]{ChangeTriggerType.MoviePlayback}));
|
||||
|
||||
//Latest
|
||||
LatestItemsQuery latestMovies = new LatestItemsQuery();
|
||||
latestMovies.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestMovies.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestMovies.setParentId(mFolder.getId());
|
||||
latestMovies.setLimit(50);
|
||||
latestMovies.setImageTypeLimit(1);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest), latestMovies, new ChangeTriggerType[] {ChangeTriggerType.MoviePlayback, ChangeTriggerType.LibraryUpdated}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest), latestMovies, new ChangeTriggerType[]{ChangeTriggerType.MoviePlayback, ChangeTriggerType.LibraryUpdated}));
|
||||
|
||||
//Favorites
|
||||
StdItemQuery favorites = new StdItemQuery();
|
||||
@@ -95,7 +79,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
favorites.setImageTypeLimit(1);
|
||||
favorites.setFilters(new ItemFilter[]{ItemFilter.IsFavorite});
|
||||
favorites.setSortBy(new String[]{ItemSortBy.SortName});
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_favorites), favorites, 60, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated, ChangeTriggerType.FavoriteUpdate}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_favorites), favorites, 60, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated, ChangeTriggerType.FavoriteUpdate}));
|
||||
|
||||
//Collections
|
||||
StdItemQuery collections = new StdItemQuery();
|
||||
@@ -104,7 +88,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
collections.setImageTypeLimit(1);
|
||||
//collections.setParentId(mFolder.getId());
|
||||
collections.setSortBy(new String[]{ItemSortBy.SortName});
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_collections), collections, 60, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_collections), collections, 60, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated}));
|
||||
|
||||
rowLoader.loadRows(mRows);
|
||||
break;
|
||||
@@ -117,8 +101,8 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
nextUpQuery.setLimit(50);
|
||||
nextUpQuery.setParentId(mFolder.getId());
|
||||
nextUpQuery.setImageTypeLimit(1);
|
||||
nextUpQuery.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
mRows.add(new BrowseRowDef(mApplication.getResources().getString(R.string.lbl_next_up), nextUpQuery, new ChangeTriggerType[] {ChangeTriggerType.TvPlayback}));
|
||||
nextUpQuery.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
mRows.add(new BrowseRowDef(mApplication.getResources().getString(R.string.lbl_next_up), nextUpQuery, new ChangeTriggerType[]{ChangeTriggerType.TvPlayback}));
|
||||
|
||||
//Premieres
|
||||
if (mApplication.getPrefs().getBoolean("pref_enable_premieres", false)) {
|
||||
@@ -140,13 +124,13 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
|
||||
//Latest content added
|
||||
LatestItemsQuery latestSeries = new LatestItemsQuery();
|
||||
latestSeries.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestSeries.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestSeries.setIncludeItemTypes(new String[]{"Episode"});
|
||||
latestSeries.setGroupItems(true);
|
||||
latestSeries.setParentId(mFolder.getId());
|
||||
latestSeries.setLimit(50);
|
||||
latestSeries.setImageTypeLimit(1);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest), latestSeries, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest), latestSeries, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated}));
|
||||
|
||||
//Favorites
|
||||
StdItemQuery tvFavorites = new StdItemQuery();
|
||||
@@ -156,7 +140,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
tvFavorites.setImageTypeLimit(1);
|
||||
tvFavorites.setFilters(new ItemFilter[]{ItemFilter.IsFavorite});
|
||||
tvFavorites.setSortBy(new String[]{ItemSortBy.SortName});
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_favorites), tvFavorites, 60, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated, ChangeTriggerType.FavoriteUpdate}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_favorites), tvFavorites, 60, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated, ChangeTriggerType.FavoriteUpdate}));
|
||||
|
||||
rowLoader.loadRows(mRows);
|
||||
break;
|
||||
@@ -164,13 +148,13 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
|
||||
//Latest
|
||||
LatestItemsQuery latestAlbums = new LatestItemsQuery();
|
||||
latestAlbums.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestAlbums.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestAlbums.setIncludeItemTypes(new String[]{"Audio"});
|
||||
latestAlbums.setGroupItems(true);
|
||||
latestAlbums.setImageTypeLimit(1);
|
||||
latestAlbums.setParentId(mFolder.getId());
|
||||
latestAlbums.setLimit(50);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest), latestAlbums, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest), latestAlbums, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated}));
|
||||
|
||||
//Last Played
|
||||
StdItemQuery lastPlayed = new StdItemQuery();
|
||||
@@ -183,7 +167,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
lastPlayed.setSortOrder(SortOrder.Descending);
|
||||
lastPlayed.setEnableTotalRecordCount(false);
|
||||
lastPlayed.setLimit(50);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_last_played), lastPlayed, 0, false, true, new ChangeTriggerType[] {ChangeTriggerType.MusicPlayback, ChangeTriggerType.LibraryUpdated}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_last_played), lastPlayed, 0, false, true, new ChangeTriggerType[]{ChangeTriggerType.MusicPlayback, ChangeTriggerType.LibraryUpdated}));
|
||||
|
||||
//Favorites
|
||||
StdItemQuery favAlbums = new StdItemQuery();
|
||||
@@ -193,17 +177,16 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
favAlbums.setImageTypeLimit(1);
|
||||
favAlbums.setFilters(new ItemFilter[]{ItemFilter.IsFavorite});
|
||||
favAlbums.setSortBy(new String[]{ItemSortBy.SortName});
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_favorites), favAlbums, 60, false, true, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated, ChangeTriggerType.FavoriteUpdate}));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_favorites), favAlbums, 60, false, true, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated, ChangeTriggerType.FavoriteUpdate}));
|
||||
|
||||
//AudioPlaylists
|
||||
StdItemQuery playlists = new StdItemQuery(new ItemFields[] {ItemFields.PrimaryImageAspectRatio, ItemFields.CumulativeRunTimeTicks});
|
||||
StdItemQuery playlists = new StdItemQuery(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.CumulativeRunTimeTicks});
|
||||
playlists.setIncludeItemTypes(new String[]{"Playlist"});
|
||||
playlists.setMediaTypes(new String[] {"Audio"});
|
||||
playlists.setImageTypeLimit(1);
|
||||
playlists.setRecursive(true);
|
||||
playlists.setSortBy(new String[]{ItemSortBy.DateCreated});
|
||||
playlists.setSortOrder(SortOrder.Descending);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_playlists), playlists, 60, false, true, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated}, QueryType.AudioPlaylists));
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_playlists), playlists, 60, false, true, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated}, QueryType.AudioPlaylists));
|
||||
|
||||
rowLoader.loadRows(mRows);
|
||||
break;
|
||||
@@ -214,7 +197,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
//On now
|
||||
RecommendedProgramQuery onNow = new RecommendedProgramQuery();
|
||||
onNow.setIsAiring(true);
|
||||
onNow.setFields(new ItemFields[] {ItemFields.Overview, ItemFields.PrimaryImageAspectRatio, ItemFields.ChannelInfo});
|
||||
onNow.setFields(new ItemFields[]{ItemFields.Overview, ItemFields.PrimaryImageAspectRatio, ItemFields.ChannelInfo});
|
||||
onNow.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
onNow.setImageTypeLimit(1);
|
||||
onNow.setEnableTotalRecordCount(false);
|
||||
@@ -261,7 +244,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
|
||||
// Also get scheduled recordings for next 24 hours
|
||||
final TimerQuery scheduled = new TimerQuery();
|
||||
TvApp.getApplication().getApiClient().GetLiveTvTimersAsync(scheduled, new Response<TimerInfoDtoResult>(){
|
||||
TvApp.getApplication().getApiClient().GetLiveTvTimersAsync(scheduled, new Response<TimerInfoDtoResult>() {
|
||||
@Override
|
||||
public void onResponse(TimerInfoDtoResult response) {
|
||||
List<BaseItemDto> nearTimers = new ArrayList<>();
|
||||
@@ -274,9 +257,9 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
programInfo = new BaseItemDto();
|
||||
programInfo.setId(timer.getId());
|
||||
programInfo.setChannelName(timer.getChannelName());
|
||||
programInfo.setName(Utils.getSafeValue(timer.getName(),"Unknown"));
|
||||
programInfo.setName(Utils.getSafeValue(timer.getName(), "Unknown"));
|
||||
TvApp.getApplication().getLogger().Warn("No program info for timer %s. Creating one...", programInfo.getName());
|
||||
programInfo.setType("Program");
|
||||
programInfo.setBaseItemType(BaseItemType.Program);
|
||||
programInfo.setTimerId(timer.getId());
|
||||
programInfo.setSeriesTimerId(timer.getSeriesTimerId());
|
||||
programInfo.setStartDate(timer.getStartDate());
|
||||
@@ -309,11 +292,11 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
recordings.setFields(new ItemFields[]{ItemFields.Overview, ItemFields.PrimaryImageAspectRatio});
|
||||
recordings.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
recordings.setEnableImages(true);
|
||||
mRows.add(new BrowseRowDef("Recent Recordings", recordings, 50));
|
||||
mRows.add(new BrowseRowDef(mActivity.getString(R.string.lbl_recent_recordings), recordings, 50));
|
||||
//All Recordings by group - will only be there for non-internal TV
|
||||
RecordingGroupQuery recordingGroups = new RecordingGroupQuery();
|
||||
recordingGroups.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
mRows.add(new BrowseRowDef("All Recordings", recordingGroups));
|
||||
mRows.add(new BrowseRowDef(mActivity.getString(R.string.lbl_all_recordings), recordingGroups));
|
||||
rowLoader.loadRows(mRows);
|
||||
|
||||
//Now insert our smart rows
|
||||
@@ -384,7 +367,7 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
ItemQuery rowQuery = new StdItemQuery();
|
||||
rowQuery.setParentId(item.getId());
|
||||
rowQuery.setUserId(userId);
|
||||
rows.add(new BrowseRowDef(item.getName(), rowQuery, 60, new ChangeTriggerType[] {ChangeTriggerType.LibraryUpdated}));
|
||||
rows.add(new BrowseRowDef(item.getName(), rowQuery, 60, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,8 +381,6 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -410,10 +391,10 @@ public class BrowseViewFragment extends EnhancedBrowseFragment {
|
||||
|
||||
GridButtonPresenter mGridPresenter = new GridButtonPresenter();
|
||||
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
|
||||
gridRowAdapter.add(new GridButton(TvApp.LIVE_TV_RECORDINGS_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_recorded_tv), R.drawable.recgroup));
|
||||
gridRowAdapter.add(new GridButton(TvApp.LIVE_TV_RECORDINGS_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_recorded_tv), R.drawable.tile_port_record));
|
||||
if (mApplication.canManageRecordings()) {
|
||||
gridRowAdapter.add(new GridButton(TvApp.LIVE_TV_SCHEDULE_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_schedule), R.drawable.clock));
|
||||
gridRowAdapter.add(new GridButton(TvApp.LIVE_TV_SERIES_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_series), R.drawable.seriestimerp));
|
||||
gridRowAdapter.add(new GridButton(TvApp.LIVE_TV_SCHEDULE_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_schedule), R.drawable.tile_port_time));
|
||||
gridRowAdapter.add(new GridButton(TvApp.LIVE_TV_SERIES_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_series), R.drawable.tile_port_series_timer));
|
||||
}
|
||||
|
||||
mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.app.Fragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -40,7 +40,7 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class CustomBrowseFragment extends Fragment implements IRowLoader {
|
||||
private RowsFragment mRowsFragment;
|
||||
private RowsSupportFragment mRowsFragment;
|
||||
private static final int BACKGROUND_UPDATE_DELAY = 100;
|
||||
|
||||
protected String MainTitle;
|
||||
@@ -65,13 +65,13 @@ public class CustomBrowseFragment extends Fragment implements IRowLoader {
|
||||
|
||||
View root = inflater.inflate(R.layout.fragment_custom_browse, container, false);
|
||||
|
||||
// Inject the RowsFragment in the results container
|
||||
// Inject the RowsSupportFragment in the results container
|
||||
if (getChildFragmentManager().findFragmentById(R.id.rows_area) == null) {
|
||||
mRowsFragment = new RowsFragment();
|
||||
mRowsFragment = new RowsSupportFragment();
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.rows_area, mRowsFragment).commit();
|
||||
} else {
|
||||
mRowsFragment = (RowsFragment) getChildFragmentManager()
|
||||
mRowsFragment = (RowsSupportFragment) getChildFragmentManager()
|
||||
.findFragmentById(R.id.rows_area);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.app.Fragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.ClassPresenterSelector;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.ClassPresenterSelector;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -60,6 +60,7 @@ import java.util.TimerTask;
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.entities.DisplayPreferences;
|
||||
|
||||
public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
@@ -95,7 +96,7 @@ public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
private SimpleTarget<Bitmap> mBackgroundTarget;
|
||||
private DisplayMetrics mMetrics;
|
||||
|
||||
RowsFragment mRowsFragment;
|
||||
RowsSupportFragment mRowsFragment;
|
||||
protected CompositeClickedListener mClickedListener = new CompositeClickedListener();
|
||||
protected CompositeSelectedListener mSelectedListener = new CompositeSelectedListener();
|
||||
protected ArrayObjectAdapter mRowsAdapter;
|
||||
@@ -112,7 +113,7 @@ public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
super.onCreate(savedInstanceState);
|
||||
BaseItemDto item = new BaseItemDto();
|
||||
item.setId(ItemListActivity.FAV_SONGS);
|
||||
item.setType("Playlist");
|
||||
item.setBaseItemType(BaseItemType.Playlist);
|
||||
item.setIsFolder(true);
|
||||
|
||||
favSongsRowItem = new BaseRowItem(0, item);
|
||||
@@ -135,13 +136,13 @@ public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
mSummary.setTypeface(roboto);
|
||||
mSummary.setShadowLayer(5, 5, 5, Color.BLACK);
|
||||
|
||||
// Inject the RowsFragment in the results container
|
||||
// Inject the RowsSupportFragment in the results container
|
||||
if (getChildFragmentManager().findFragmentById(R.id.rowsFragment) == null) {
|
||||
mRowsFragment = new RowsFragment();
|
||||
mRowsFragment = new RowsSupportFragment();
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.rowsFragment, mRowsFragment).commit();
|
||||
} else {
|
||||
mRowsFragment = (RowsFragment) getChildFragmentManager()
|
||||
mRowsFragment = (RowsSupportFragment) getChildFragmentManager()
|
||||
.findFragmentById(R.id.rowsFragment);
|
||||
}
|
||||
|
||||
@@ -326,14 +327,14 @@ public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
|
||||
switch (itemTypeString) {
|
||||
case "Movie":
|
||||
gridRowAdapter.add(new GridButton(SUGGESTED, mApplication.getString(R.string.lbl_suggested), R.drawable.suggestions));
|
||||
gridRowAdapter.add(new GridButton(SUGGESTED, mApplication.getString(R.string.lbl_suggested), R.drawable.tile_suggestions));
|
||||
addStandardViewButtons(gridRowAdapter);
|
||||
break;
|
||||
case "MusicAlbum":
|
||||
gridRowAdapter.add(new GridButton(ALBUMS, TvApp.getApplication().getString(R.string.lbl_albums), R.drawable.audio));
|
||||
gridRowAdapter.add(new GridButton(ARTISTS, TvApp.getApplication().getString(R.string.lbl_artists), R.drawable.artists));
|
||||
gridRowAdapter.add(new GridButton(GENRES, mApplication.getString(R.string.lbl_genres), R.drawable.genres));
|
||||
gridRowAdapter.add(new GridButton(SEARCH, mApplication.getString(R.string.lbl_search), R.drawable.search));
|
||||
gridRowAdapter.add(new GridButton(ALBUMS, TvApp.getApplication().getString(R.string.lbl_albums), R.drawable.tile_audio));
|
||||
gridRowAdapter.add(new GridButton(ARTISTS, TvApp.getApplication().getString(R.string.lbl_artists), R.drawable.tile_artists));
|
||||
gridRowAdapter.add(new GridButton(GENRES, mApplication.getString(R.string.lbl_genres), R.drawable.tile_genres));
|
||||
gridRowAdapter.add(new GridButton(SEARCH, mApplication.getString(R.string.lbl_search), R.drawable.tile_search));
|
||||
break;
|
||||
default:
|
||||
addStandardViewButtons(gridRowAdapter);
|
||||
@@ -346,11 +347,11 @@ public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
}
|
||||
|
||||
protected void addStandardViewButtons(ArrayObjectAdapter gridRowAdapter) {
|
||||
gridRowAdapter.add(new GridButton(GRID, TvApp.getApplication().getString(R.string.lbl_all_items), R.drawable.grid));
|
||||
gridRowAdapter.add(new GridButton(BY_LETTER, mApplication.getString(R.string.lbl_by_letter), R.drawable.byletter));
|
||||
gridRowAdapter.add(new GridButton(GENRES, mApplication.getString(R.string.lbl_genres), R.drawable.genres));
|
||||
//gridRowAdapter.add(new GridButton(PERSONS, mApplication.getString(R.string.lbl_performers), R.drawable.actors));
|
||||
gridRowAdapter.add(new GridButton(SEARCH, mApplication.getString(R.string.lbl_search), R.drawable.search));
|
||||
gridRowAdapter.add(new GridButton(GRID, TvApp.getApplication().getString(R.string.lbl_all_items), R.drawable.tile_port_grid));
|
||||
gridRowAdapter.add(new GridButton(BY_LETTER, mApplication.getString(R.string.lbl_by_letter), R.drawable.tile_letters));
|
||||
gridRowAdapter.add(new GridButton(GENRES, mApplication.getString(R.string.lbl_genres), R.drawable.tile_genres));
|
||||
//gridRowAdapter.add(new GridButton(PERSONS, mApplication.getString(R.string.lbl_performers), R.drawable.tile_actors));
|
||||
gridRowAdapter.add(new GridButton(SEARCH, mApplication.getString(R.string.lbl_search), R.drawable.tile_search));
|
||||
|
||||
}
|
||||
|
||||
@@ -388,8 +389,8 @@ public class EnhancedBrowseFragment extends Fragment implements IRowLoader {
|
||||
}
|
||||
|
||||
private void refreshCurrentItem() {
|
||||
if (mCurrentItem != null && !"Photo".equals(mCurrentItem.getType()) && !"MusicArtist".equals(mCurrentItem.getType())
|
||||
&& !"MusicAlbum".equals(mCurrentItem.getType()) && !"Playlist".equals(mCurrentItem.getType())) {
|
||||
if (mCurrentItem != null && mCurrentItem.getBaseItemType() != BaseItemType.Photo && mCurrentItem.getBaseItemType() != BaseItemType.MusicArtist
|
||||
&& mCurrentItem.getBaseItemType() != BaseItemType.MusicAlbum && mCurrentItem.getBaseItemType() != BaseItemType.Playlist) {
|
||||
mCurrentItem.refresh(new EmptyResponse() {
|
||||
@Override
|
||||
public void onResponse() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.jellyfin.androidtv.querying.StdItemQuery;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.entities.SortOrder;
|
||||
import org.jellyfin.apiclient.model.livetv.RecordingQuery;
|
||||
import org.jellyfin.apiclient.model.querying.ItemFields;
|
||||
@@ -23,12 +24,12 @@ public class GenericFolderFragment extends EnhancedBrowseFragment {
|
||||
|
||||
}
|
||||
|
||||
private static String[] showSpecialViewTypes = new String[] {"CollectionFolder", "Folder", "UserView", "ChannelFolderItem"};
|
||||
private static BaseItemType[] showSpecialViewTypes = new BaseItemType[] {BaseItemType.CollectionFolder, BaseItemType.Folder, BaseItemType.UserView, BaseItemType.ChannelFolderItem };
|
||||
|
||||
@Override
|
||||
protected void setupQueries(IRowLoader rowLoader) {
|
||||
|
||||
if (mFolder.getType().equals("RecordingGroup")){
|
||||
if (mFolder.getBaseItemType() == BaseItemType.RecordingGroup){
|
||||
RecordingQuery query = new RecordingQuery();
|
||||
query.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
query.setGroupId(mFolder.getId());
|
||||
@@ -38,14 +39,14 @@ public class GenericFolderFragment extends EnhancedBrowseFragment {
|
||||
} else {
|
||||
|
||||
if (Utils.getSafeValue(mFolder.getChildCount(), 0) > 0 ||
|
||||
mFolder.getType().equals("Channel") ||
|
||||
mFolder.getType().equals("ChannelFolderItem") ||
|
||||
mFolder.getType().equals("UserView") ||
|
||||
mFolder.getType().equals("CollectionFolder")) {
|
||||
boolean showSpecialViews = Arrays.asList(showSpecialViewTypes).contains(mFolder.getType()) && !"channels".equals(mFolder.getCollectionType());
|
||||
mFolder.getBaseItemType() == BaseItemType.Channel ||
|
||||
mFolder.getBaseItemType() == BaseItemType.ChannelFolderItem ||
|
||||
mFolder.getBaseItemType() == BaseItemType.UserView ||
|
||||
mFolder.getBaseItemType() == BaseItemType.CollectionFolder) {
|
||||
boolean showSpecialViews = Arrays.asList(showSpecialViewTypes).contains(mFolder.getBaseItemType()) && !"channels".equals(mFolder.getCollectionType());
|
||||
|
||||
if (showSpecialViews) {
|
||||
if (!mFolder.getType().equals("ChannelFolderItem")) {
|
||||
if (mFolder.getBaseItemType() != BaseItemType.ChannelFolderItem) {
|
||||
StdItemQuery resume = new StdItemQuery();
|
||||
resume.setParentId(mFolder.getId());
|
||||
resume.setLimit(50);
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.base.CustomMessage;
|
||||
import org.jellyfin.androidtv.base.IMessageListener;
|
||||
import org.jellyfin.androidtv.integration.RecommendationManager;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
import org.jellyfin.androidtv.livetv.LiveTvGuideActivity;
|
||||
import org.jellyfin.androidtv.model.ChangeTriggerType;
|
||||
import org.jellyfin.androidtv.model.DisplayPriorityType;
|
||||
import org.jellyfin.androidtv.playback.AudioEventListener;
|
||||
import org.jellyfin.androidtv.playback.MediaManager;
|
||||
import org.jellyfin.androidtv.presentation.GridButtonPresenter;
|
||||
import org.jellyfin.androidtv.presentation.ThemeManager;
|
||||
import org.jellyfin.androidtv.querying.QueryType;
|
||||
import org.jellyfin.androidtv.querying.StdItemQuery;
|
||||
import org.jellyfin.androidtv.querying.ViewQuery;
|
||||
import org.jellyfin.androidtv.settings.SettingsActivity;
|
||||
import org.jellyfin.androidtv.startup.LogonCredentials;
|
||||
import org.jellyfin.androidtv.startup.SelectUserActivity;
|
||||
import org.jellyfin.androidtv.ui.GridButton;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.entities.LocationType;
|
||||
import org.jellyfin.apiclient.model.entities.SortOrder;
|
||||
import org.jellyfin.apiclient.model.livetv.RecommendedProgramQuery;
|
||||
import org.jellyfin.apiclient.model.livetv.RecordingQuery;
|
||||
import org.jellyfin.apiclient.model.querying.ItemFields;
|
||||
import org.jellyfin.apiclient.model.querying.ItemFilter;
|
||||
import org.jellyfin.apiclient.model.querying.ItemSortBy;
|
||||
import org.jellyfin.apiclient.model.querying.ItemsResult;
|
||||
import org.jellyfin.apiclient.model.querying.LatestItemsQuery;
|
||||
import org.jellyfin.apiclient.model.querying.NextUpQuery;
|
||||
|
||||
/**
|
||||
* Created by Eric on 12/4/2014.
|
||||
*/
|
||||
public class HomeFragment extends StdBrowseFragment {
|
||||
private static final int LOGOUT = 0;
|
||||
private static final int SETTINGS = 1;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
|
||||
MainTitle = this.getString(R.string.home_title);
|
||||
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
//Save last login so we can get back proper context on entry
|
||||
try {
|
||||
AuthenticationHelper.saveLoginCredentials(new LogonCredentials(TvApp.getApplication().getApiClient().getServerInfo(), TvApp.getApplication().getCurrentUser()), TvApp.CREDENTIALS_PATH);
|
||||
} catch (IOException e) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Unable to save login creds", e);
|
||||
}
|
||||
|
||||
//Init recommendations
|
||||
RecommendationManager.init();
|
||||
|
||||
//Get auto bitrate
|
||||
TvApp.getApplication().determineAutoBitrate();
|
||||
|
||||
//First time audio message
|
||||
if (!mApplication.getSystemPrefs().getBoolean("syspref_audio_warned", false)) {
|
||||
mApplication.getSystemPrefs().edit().putBoolean("syspref_audio_warned",true).apply();
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setTitle(mApplication.getString(R.string.lbl_audio_capabilitites))
|
||||
.setMessage(mApplication.getString(R.string.msg_audio_warning))
|
||||
.setPositiveButton(mApplication.getString(R.string.btn_got_it), null)
|
||||
.setNegativeButton(mApplication.getString(R.string.btn_set_compatible_audio), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mApplication.getPrefs().edit().putString("pref_audio_option", "1").apply();
|
||||
}
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
ThemeManager.showWelcomeMessage();
|
||||
|
||||
//Subscribe to Audio messages
|
||||
MediaManager.addAudioEventListener(audioEventListener);
|
||||
|
||||
//Setup activity messages
|
||||
mActivity.registerMessageListener(new IMessageListener() {
|
||||
@Override
|
||||
public void onMessageReceived(CustomMessage message) {
|
||||
switch (message) {
|
||||
case RefreshRows:
|
||||
if (hasResumeRow()) {
|
||||
refreshRows();
|
||||
} else {
|
||||
addContinueWatching();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (mApplication.getPrefs().getBoolean("pref_live_tv_mode",false)) {
|
||||
//open guide activity and tell it to start last channel
|
||||
Intent guide = new Intent(getActivity(), LiveTvGuideActivity.class);
|
||||
guide.putExtra("loadLast", true);
|
||||
|
||||
startActivity(guide);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
//make sure rows have had a chance to be created
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addNowPlaying();
|
||||
//check for resume row and add if not there
|
||||
if (!hasResumeRow()) addContinueWatching();
|
||||
}
|
||||
}, 750);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
MediaManager.removeAudioEventListener(audioEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupQueries(final IRowLoader rowLoader) {
|
||||
//Peek at the first library item to determine our order
|
||||
TvApp.getApplication().getApiClient().GetUserViews(TvApp.getApplication().getCurrentUser().getId(), new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult response) {
|
||||
//First library and in-progress
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_library), new ViewQuery()));
|
||||
|
||||
//Special suggestions
|
||||
String[] specialGenres = ThemeManager.getSpecialGenres();
|
||||
if (specialGenres != null) {
|
||||
StdItemQuery suggestions = new StdItemQuery();
|
||||
suggestions.setIncludeItemTypes(new String[]{"Movie", "Series"});
|
||||
suggestions.setGenres(specialGenres);
|
||||
suggestions.setRecursive(true);
|
||||
suggestions.setLimit(40);
|
||||
suggestions.setEnableTotalRecordCount(false);
|
||||
suggestions.setSortBy(new String[]{ItemSortBy.DatePlayed});
|
||||
suggestions.setSortOrder(SortOrder.Ascending);
|
||||
mRows.add(new BrowseRowDef(ThemeManager.getSuggestionTitle(), suggestions, 0, true, true, new ChangeTriggerType[]{}));
|
||||
|
||||
}
|
||||
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_continue_watching), getResumeQuery(), 0, true, true, new ChangeTriggerType[]{ChangeTriggerType.MoviePlayback, ChangeTriggerType.TvPlayback, ChangeTriggerType.VideoQueueChange}, QueryType.ContinueWatching));
|
||||
|
||||
//Now others based on first library type
|
||||
if (response.getTotalRecordCount() > 0) {
|
||||
mApplication.setDisplayPriority("tvshows".equals(response.getItems()[0].getCollectionType()) ? DisplayPriorityType.TvShows : ("livetv".equals(response.getItems()[0].getCollectionType()) ? DisplayPriorityType.LiveTv : DisplayPriorityType.Movies));
|
||||
switch (mApplication.getDisplayPriority()) {
|
||||
case TvShows:
|
||||
addNextUp();
|
||||
addPremieres();
|
||||
addLatestMovies();
|
||||
addOnNow();
|
||||
break;
|
||||
|
||||
case LiveTv:
|
||||
addOnNow();
|
||||
addNextUp();
|
||||
addPremieres();
|
||||
addLatestMovies();
|
||||
break;
|
||||
|
||||
default:
|
||||
addLatestMovies();
|
||||
addNextUp();
|
||||
addPremieres();
|
||||
addOnNow();
|
||||
}
|
||||
}
|
||||
// StdItemQuery latestMusic = new StdItemQuery();
|
||||
// latestMusic.setIncludeItemTypes(new String[]{"MusicAlbum"});
|
||||
// latestMusic.setRecursive(true);
|
||||
// latestMusic.setLimit(50);
|
||||
// latestMusic.setSortBy(new String[]{ItemSortBy.DateCreated});
|
||||
// latestMusic.setSortOrder(SortOrder.Descending);
|
||||
// mRowDef.add(new BrowseRowDef("Latest Albums", latestMusic, 0));
|
||||
|
||||
rowLoader.loadRows(mRows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected StdItemQuery getResumeQuery() {
|
||||
StdItemQuery resumeItems = new StdItemQuery();
|
||||
resumeItems.setMediaTypes(new String[] {"Video"});
|
||||
resumeItems.setRecursive(true);
|
||||
resumeItems.setImageTypeLimit(1);
|
||||
resumeItems.setEnableTotalRecordCount(false);
|
||||
resumeItems.setCollapseBoxSetItems(false);
|
||||
resumeItems.setExcludeLocationTypes(new LocationType[] {LocationType.Virtual});
|
||||
resumeItems.setLimit(50);
|
||||
resumeItems.setFilters(new ItemFilter[]{ItemFilter.IsResumable});
|
||||
resumeItems.setSortBy(new String[]{ItemSortBy.DatePlayed});
|
||||
resumeItems.setSortOrder(SortOrder.Descending);
|
||||
return resumeItems;
|
||||
}
|
||||
|
||||
protected void addLatestMovies() {
|
||||
LatestItemsQuery latestMovies = new LatestItemsQuery();
|
||||
latestMovies.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
latestMovies.setIncludeItemTypes(new String[]{"Movie"});
|
||||
latestMovies.setImageTypeLimit(1);
|
||||
latestMovies.setLimit(50);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_latest_movies), latestMovies, new ChangeTriggerType[]{ChangeTriggerType.LibraryUpdated, ChangeTriggerType.MoviePlayback}));
|
||||
|
||||
}
|
||||
|
||||
protected void addNextUp() {
|
||||
NextUpQuery nextUpQuery = new NextUpQuery();
|
||||
nextUpQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
nextUpQuery.setImageTypeLimit(1);
|
||||
nextUpQuery.setLimit(50);
|
||||
nextUpQuery.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_next_up_tv), nextUpQuery, new ChangeTriggerType[] {ChangeTriggerType.TvPlayback}));
|
||||
|
||||
}
|
||||
|
||||
protected void addPremieres() {
|
||||
if (mApplication.getPrefs().getBoolean("pref_enable_premieres", false)) {
|
||||
StdItemQuery newQuery = new StdItemQuery(new ItemFields[]{ItemFields.DateCreated, ItemFields.PrimaryImageAspectRatio, ItemFields.Overview});
|
||||
newQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
newQuery.setIncludeItemTypes(new String[]{"Episode"});
|
||||
newQuery.setRecursive(true);
|
||||
newQuery.setIsVirtualUnaired(false);
|
||||
newQuery.setIsMissing(false);
|
||||
newQuery.setImageTypeLimit(1);
|
||||
newQuery.setFilters(new ItemFilter[]{ItemFilter.IsUnplayed});
|
||||
newQuery.setSortBy(new String[]{ItemSortBy.DateCreated});
|
||||
newQuery.setSortOrder(SortOrder.Descending);
|
||||
newQuery.setEnableTotalRecordCount(false);
|
||||
newQuery.setLimit(200);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_new_premieres), newQuery, 0, true, true, new ChangeTriggerType[] {ChangeTriggerType.TvPlayback}, QueryType.Premieres));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void addOnNow() {
|
||||
if (TvApp.getApplication().getCurrentUser().getPolicy().getEnableLiveTvAccess()) {
|
||||
RecommendedProgramQuery onNow = new RecommendedProgramQuery();
|
||||
onNow.setIsAiring(true);
|
||||
onNow.setFields(new ItemFields[] {ItemFields.Overview, ItemFields.PrimaryImageAspectRatio, ItemFields.ChannelInfo});
|
||||
onNow.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
onNow.setImageTypeLimit(1);
|
||||
onNow.setEnableTotalRecordCount(false);
|
||||
onNow.setLimit(20);
|
||||
mRows.add(new BrowseRowDef(mApplication.getString(R.string.lbl_on_now), onNow));
|
||||
//Latest Recordings
|
||||
RecordingQuery recordings = new RecordingQuery();
|
||||
recordings.setFields(new ItemFields[]{ItemFields.Overview, ItemFields.PrimaryImageAspectRatio});
|
||||
recordings.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
recordings.setEnableImages(true);
|
||||
recordings.setLimit(40);
|
||||
mRows.add(new BrowseRowDef(mActivity.getString(R.string.lbl_recordings), recordings));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean hasResumeRow() {
|
||||
if (mRowsAdapter == null) return true;
|
||||
for (int i = 0; i < mRowsAdapter.size(); i++) {
|
||||
ListRow row = (ListRow)mRowsAdapter.get(i);
|
||||
if (row.getAdapter() instanceof ItemRowAdapter && ((ItemRowAdapter)row.getAdapter()).getQueryType().equals(QueryType.ContinueWatching)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void addContinueWatching() {
|
||||
//create the row and retrieve it to see if there are any before adding
|
||||
final ItemRowAdapter resume = new ItemRowAdapter(getResumeQuery(), 0, true, true, mCardPresenter, mRowsAdapter, QueryType.ContinueWatching);
|
||||
resume.setReRetrieveTriggers(new ChangeTriggerType[] {ChangeTriggerType.VideoQueueChange, ChangeTriggerType.TvPlayback, ChangeTriggerType.MoviePlayback});
|
||||
final ListRow row = new ListRow(new HeaderItem(mApplication.getString(R.string.lbl_continue_watching)), resume);
|
||||
resume.setRow(row);
|
||||
resume.setRetrieveFinishedListener(new EmptyResponse() {
|
||||
@Override
|
||||
public void onResponse() {
|
||||
mApplication.getLogger().Info("*** Continue watching retrieve finished: "+resume.size());
|
||||
if (resume.size() > 0) {
|
||||
mRowsAdapter.add(1, row);
|
||||
}
|
||||
}
|
||||
});
|
||||
resume.Retrieve();
|
||||
}
|
||||
|
||||
protected AudioEventListener audioEventListener = new AudioEventListener() {
|
||||
@Override
|
||||
public void onQueueStatusChanged(boolean hasQueue) {
|
||||
//remove on any change - it will re-add on resume
|
||||
if (nowPlayingRow != null) {
|
||||
mRowsAdapter.remove(nowPlayingRow);
|
||||
nowPlayingRow = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected ListRow nowPlayingRow;
|
||||
|
||||
protected void addNowPlaying() {
|
||||
if (MediaManager.isPlayingAudio()) {
|
||||
if (nowPlayingRow == null) {
|
||||
nowPlayingRow = new ListRow(new HeaderItem(getString(R.string.lbl_now_playing)), MediaManager.getManagedAudioQueue());
|
||||
mRowsAdapter.add(1, nowPlayingRow);
|
||||
}
|
||||
} else {
|
||||
if (nowPlayingRow != null) {
|
||||
mRowsAdapter.remove(nowPlayingRow);
|
||||
nowPlayingRow = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalRows(ArrayObjectAdapter rowAdapter) {
|
||||
super.addAdditionalRows(rowAdapter);
|
||||
|
||||
HeaderItem gridHeader = new HeaderItem(rowAdapter.size(), mApplication.getString(R.string.lbl_settings));
|
||||
|
||||
GridButtonPresenter mGridPresenter = new GridButtonPresenter();
|
||||
ArrayObjectAdapter toolsRow = new ArrayObjectAdapter(mGridPresenter);
|
||||
toolsRow.add(new GridButton(SETTINGS, mApplication.getString(R.string.lbl_app_settings), R.drawable.gears));
|
||||
toolsRow.add(new GridButton(LOGOUT, mApplication.getString(R.string.lbl_logout) + TvApp.getApplication().getCurrentUser().getName(), R.drawable.logout));
|
||||
rowAdapter.add(new ListRow(gridHeader, toolsRow));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupEventListeners() {
|
||||
super.setupEventListeners();
|
||||
mClickedListener.registerListener(new ItemViewClickedListener());
|
||||
}
|
||||
|
||||
private final class ItemViewClickedListener implements OnItemViewClickedListener {
|
||||
@Override
|
||||
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
|
||||
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||
|
||||
if (item instanceof GridButton) {
|
||||
switch (((GridButton) item).getId()) {
|
||||
case LOGOUT:
|
||||
TvApp app = TvApp.getApplication();
|
||||
if (app.getIsAutoLoginConfigured()) {
|
||||
// Present user selection
|
||||
app.setLoginApiClient(app.getApiClient());
|
||||
Intent userIntent = new Intent(getActivity(), SelectUserActivity.class);
|
||||
userIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
getActivity().startActivity(userIntent);
|
||||
|
||||
}
|
||||
|
||||
getActivity().finish(); //don't actually log out because we handle it ourselves
|
||||
|
||||
break;
|
||||
case SETTINGS:
|
||||
Intent settings = new Intent(getActivity(), SettingsActivity.class);
|
||||
getActivity().startActivity(settings);
|
||||
break;
|
||||
default:
|
||||
Toast.makeText(getActivity(), item.toString(), Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.jellyfin.androidtv.browsing;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.base.BaseActivity;
|
||||
|
||||
public class MainActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_home);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.jellyfin.androidtv.browsing
|
||||
|
||||
import android.os.Bundle
|
||||
import org.jellyfin.androidtv.R
|
||||
import org.jellyfin.androidtv.base.BaseActivity
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_home)
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,16 @@ package org.jellyfin.androidtv.browsing;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.app.BrowseFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.BrowseSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
@@ -67,8 +67,9 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
|
||||
public class StdBrowseFragment extends BrowseFragment implements IRowLoader {
|
||||
public class StdBrowseFragment extends BrowseSupportFragment implements IRowLoader {
|
||||
private static final String TAG = "StdBrowseFragment";
|
||||
|
||||
private static final int BACKGROUND_UPDATE_DELAY = 100;
|
||||
@@ -90,7 +91,7 @@ public class StdBrowseFragment extends BrowseFragment implements IRowLoader {
|
||||
private final Handler mHandler = new Handler();
|
||||
private String mBackgroundUrl;
|
||||
protected ArrayList<BrowseRowDef> mRows = new ArrayList<>();
|
||||
CardPresenter mCardPresenter;
|
||||
protected CardPresenter mCardPresenter;
|
||||
|
||||
protected boolean justLoaded = true;
|
||||
|
||||
@@ -262,7 +263,7 @@ public class StdBrowseFragment extends BrowseFragment implements IRowLoader {
|
||||
}
|
||||
|
||||
protected void setupUIElements() {
|
||||
if (ShowBadge) setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.logob400));
|
||||
if (ShowBadge) setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.app_logo_transparent));
|
||||
setTitle(MainTitle); // Badge, when set, takes precedent over title
|
||||
setHeadersState(HEADERS_ENABLED);
|
||||
setHeadersTransitionOnBackEnabled(true);
|
||||
@@ -400,7 +401,7 @@ public class StdBrowseFragment extends BrowseFragment implements IRowLoader {
|
||||
}
|
||||
|
||||
private void refreshCurrentItem() {
|
||||
if (mCurrentItem != null && !mCurrentItem.getType().equals("UserView") && !mCurrentItem.getType().equals("CollectionFolder")) {
|
||||
if (mCurrentItem != null && mCurrentItem.getBaseItemType() != BaseItemType.UserView && mCurrentItem.getBaseItemType() != BaseItemType.CollectionFolder) {
|
||||
TvApp.getApplication().getLogger().Debug("Refresh item "+mCurrentItem.getFullName());
|
||||
mCurrentItem.refresh(new EmptyResponse() {
|
||||
@Override
|
||||
@@ -452,9 +453,11 @@ public class StdBrowseFragment extends BrowseFragment implements IRowLoader {
|
||||
mCurrentRow = (ListRow) row;
|
||||
BaseRowItem rowItem = (BaseRowItem) item;
|
||||
|
||||
//mApplication.getLogger().Debug("Selected Item "+rowItem.getIndex() + " type: "+ (rowItem.getItemType().equals(BaseRowItem.ItemType.BaseItem) ? rowItem.getBaseItem().getType() : "other"));
|
||||
ItemRowAdapter adapter = (ItemRowAdapter) ((ListRow)row).getAdapter();
|
||||
adapter.loadMoreItemsIfNeeded(rowItem.getIndex());
|
||||
if (((ListRow) row).getAdapter() instanceof ItemRowAdapter) {
|
||||
//mApplication.getLogger().Debug("Selected Item "+rowItem.getIndex() + " type: "+ (rowItem.getItemType().equals(BaseRowItem.ItemType.BaseItem) ? rowItem.getBaseItem().getType() : "other"));
|
||||
ItemRowAdapter adapter = (ItemRowAdapter) ((ListRow) row).getAdapter();
|
||||
adapter.loadMoreItemsIfNeeded(rowItem.getIndex());
|
||||
}
|
||||
|
||||
if (ShowFanart) {
|
||||
mBackgroundUrl = rowItem.getBackdropImageUrl();
|
||||
|
||||
@@ -17,16 +17,16 @@ package org.jellyfin.androidtv.browsing;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
@@ -74,6 +74,7 @@ import java.util.TimerTask;
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.entities.DisplayPreferences;
|
||||
|
||||
public class StdGridFragment extends HorizontalGridFragment implements IGridLoader {
|
||||
@@ -370,7 +371,7 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
}
|
||||
});
|
||||
|
||||
toolBar.addView(new ImageButton(getActivity(), R.drawable.sort, size, new View.OnClickListener() {
|
||||
toolBar.addView(new ImageButton(getActivity(), R.drawable.ic_sort, size, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
//Create sort menu
|
||||
@@ -397,7 +398,7 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
}));
|
||||
|
||||
if (mRowDef.getQueryType() == QueryType.Items) {
|
||||
mUnwatchedButton = new ImageButton(getActivity(), mGridAdapter.getFilters().isUnwatchedOnly() ? R.drawable.unwatchedred : R.drawable.unwatchedwhite, size, new View.OnClickListener() {
|
||||
mUnwatchedButton = new ImageButton(getActivity(), mGridAdapter.getFilters().isUnwatchedOnly() ? R.drawable.ic_unwatch_red : R.drawable.ic_unwatch, size, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FilterOptions filters = mGridAdapter.getFilters();
|
||||
@@ -411,7 +412,7 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
} else {
|
||||
mGridAdapter.Retrieve();
|
||||
}
|
||||
mUnwatchedButton.setImageResource(filters.isUnwatchedOnly() ? R.drawable.unwatchedred : R.drawable.unwatchedwhite);
|
||||
mUnwatchedButton.setImageResource(filters.isUnwatchedOnly() ? R.drawable.ic_unwatch_red : R.drawable.ic_unwatch);
|
||||
|
||||
|
||||
}
|
||||
@@ -419,7 +420,7 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
toolBar.addView(mUnwatchedButton);
|
||||
}
|
||||
|
||||
mFavoriteButton =new ImageButton(getActivity(), mGridAdapter.getFilters().isFavoriteOnly() ? R.drawable.redheart : R.drawable.whiteheart, size, new View.OnClickListener() {
|
||||
mFavoriteButton =new ImageButton(getActivity(), mGridAdapter.getFilters().isFavoriteOnly() ? R.drawable.ic_heart_red : R.drawable.ic_heart, size, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FilterOptions filters = mGridAdapter.getFilters();
|
||||
@@ -433,13 +434,13 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
} else {
|
||||
mGridAdapter.Retrieve();
|
||||
}
|
||||
mFavoriteButton.setImageResource(filters.isFavoriteOnly() ? R.drawable.redheart : R.drawable.whiteheart);
|
||||
mFavoriteButton.setImageResource(filters.isFavoriteOnly() ? R.drawable.ic_heart_red : R.drawable.ic_heart);
|
||||
|
||||
}
|
||||
});
|
||||
toolBar.addView(mFavoriteButton);
|
||||
|
||||
mLetterButton = new ImageButton(getActivity(), R.drawable.jumpletter, size, new View.OnClickListener() {
|
||||
mLetterButton = new ImageButton(getActivity(), R.drawable.ic_jump_letter, size, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
//Open letter jump popup
|
||||
@@ -448,14 +449,14 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
});
|
||||
toolBar.addView(mLetterButton);
|
||||
|
||||
toolBar.addView(new ImageButton(getActivity(), R.drawable.search2, size, new View.OnClickListener() {
|
||||
toolBar.addView(new ImageButton(getActivity(), R.drawable.ic_search, size, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TvApp.getApplication().showSearch(getActivity(), "music".equals(mFolder.getCollectionType()) || "MusicAlbum".equals(mFolder.getType()) || "MusicArtist".equals(mFolder.getType()));
|
||||
TvApp.getApplication().showSearch(getActivity(), "music".equals(mFolder.getCollectionType()) || mFolder.getBaseItemType() == BaseItemType.MusicAlbum || mFolder.getBaseItemType() == BaseItemType.MusicArtist);
|
||||
}
|
||||
}));
|
||||
|
||||
toolBar.addView(new ImageButton(getActivity(), R.drawable.cog, size, new View.OnClickListener() {
|
||||
toolBar.addView(new ImageButton(getActivity(), R.drawable.ic_settings, size, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mDisplayPrefsPopup.show(mDisplayPrefs, mFolder.getCollectionType());
|
||||
@@ -482,7 +483,7 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
mPopup = new PopupWindow(layout, WIDTH, HEIGHT);
|
||||
mPopup.setFocusable(true);
|
||||
mPopup.setOutsideTouchable(true);
|
||||
mPopup.setBackgroundDrawable(new BitmapDrawable()); // necessary for popup to dismiss
|
||||
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // necessary for popup to dismiss
|
||||
mPopup.setAnimationStyle(R.style.PopupSlideInTop);
|
||||
|
||||
mJumplist = new JumpList(activity, new CharSelectedListener() {
|
||||
@@ -589,8 +590,8 @@ public class StdGridFragment extends HorizontalGridFragment implements IGridLoad
|
||||
getGridPresenter().setPosition(MediaManager.getCurrentMediaPosition());
|
||||
MediaManager.setCurrentMediaPosition(-1); // re-set so it doesn't mess with parent views
|
||||
}
|
||||
if (mCurrentItem != null && !"Photo".equals(mCurrentItem.getType()) && !"PhotoAlbum".equals(mCurrentItem.getType())
|
||||
&& !"MusicArtist".equals(mCurrentItem.getType()) && !"MusicAlbum".equals(mCurrentItem.getType())) {
|
||||
if (mCurrentItem != null && mCurrentItem.getBaseItemType() != BaseItemType.Photo && mCurrentItem.getBaseItemType() != BaseItemType.PhotoAlbum
|
||||
&& mCurrentItem.getBaseItemType() != BaseItemType.MusicArtist && mCurrentItem.getBaseItemType() != BaseItemType.MusicAlbum) {
|
||||
TvApp.getApplication().getLogger().Debug("Refresh item "+mCurrentItem.getFullName());
|
||||
mCurrentItem.refresh(new EmptyResponse() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.jellyfin.androidtv.channels
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.tvprovider.media.tv.TvContractCompat.WatchNextPrograms
|
||||
import androidx.tvprovider.media.tv.WatchNextProgram
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.androidtv.TvApp
|
||||
import org.jellyfin.androidtv.startup.StartupActivity
|
||||
import org.jellyfin.androidtv.util.apiclient.getNextUpEpisodes
|
||||
import org.jellyfin.apiclient.model.drawing.ImageFormat
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto
|
||||
import org.jellyfin.apiclient.model.dto.ImageOptions
|
||||
import org.jellyfin.apiclient.model.querying.ItemFields
|
||||
import org.jellyfin.apiclient.model.querying.NextUpQuery
|
||||
|
||||
/**
|
||||
* Manages channels on the android tv home screen
|
||||
*
|
||||
* More info: https://developer.android.com/training/tv/discovery/recommendations-channel
|
||||
*/
|
||||
class ChannelManager {
|
||||
private companion object {
|
||||
/**
|
||||
* Amount of ticks found in a millisecond, used for calculation
|
||||
*/
|
||||
private const val TICKS_IN_MILLISECOND = 10000
|
||||
}
|
||||
|
||||
private val application = TvApp.getApplication()
|
||||
|
||||
/**
|
||||
* Check if the app can use Leanback features and is API level 26 or higher
|
||||
*/
|
||||
private val isSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& application.packageManager.hasSystemFeature("android.software.leanback")
|
||||
|
||||
/**
|
||||
* Update all channels for the currently authenticated user
|
||||
*/
|
||||
fun update() {
|
||||
// Don't do anything if not supported
|
||||
if (!isSupported) return
|
||||
|
||||
// Launch in separate coroutine scope
|
||||
GlobalScope.launch {
|
||||
updateWatchNext()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the "watch next" row with new and unfinished episodes
|
||||
* does not include movies, music or other types of media
|
||||
*/
|
||||
private suspend fun updateWatchNext() = withContext(Dispatchers.Default) {
|
||||
// Delete current items
|
||||
application.contentResolver.delete(WatchNextPrograms.CONTENT_URI, null, null)
|
||||
|
||||
// Get user or return if no user is found (not authenticated)
|
||||
val user = application.currentUser ?: return@withContext
|
||||
|
||||
// Get new items
|
||||
val response = application.apiClient.getNextUpEpisodes(NextUpQuery().apply {
|
||||
userId = user.id
|
||||
imageTypeLimit = 1
|
||||
limit = 10
|
||||
fields = arrayOf(ItemFields.DateCreated)
|
||||
})
|
||||
|
||||
// Add new items
|
||||
response?.items?.forEach { item ->
|
||||
application.contentResolver.insert(
|
||||
WatchNextPrograms.CONTENT_URI,
|
||||
getBaseItemAsWatchNextProgram(item).toContentValues()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert [BaseItemDto] to [WatchNextProgram]
|
||||
*
|
||||
* Assumes the item type is "episode"
|
||||
*/
|
||||
private fun getBaseItemAsWatchNextProgram(item: BaseItemDto) = WatchNextProgram.Builder().apply {
|
||||
setInternalProviderId(item.id)
|
||||
setType(WatchNextPrograms.TYPE_TV_EPISODE)
|
||||
setTitle("${item.seriesName} - ${item.name}")
|
||||
|
||||
// Poster image
|
||||
setPosterArtAspectRatio(WatchNextPrograms.ASPECT_RATIO_16_9)
|
||||
setPosterArtUri(Uri.parse(application.apiClient.GetImageUrl(item, ImageOptions().apply {
|
||||
format = ImageFormat.Png
|
||||
height = 288
|
||||
width = 512
|
||||
})))
|
||||
|
||||
// Use date created or fallback to current time if unavailable
|
||||
setLastEngagementTimeUtcMillis(item.dateCreated?.time ?: System.currentTimeMillis())
|
||||
|
||||
when {
|
||||
// User has started playing the episode
|
||||
item.canResume -> {
|
||||
setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
|
||||
setLastPlaybackPositionMillis((item.resumePositionTicks / TICKS_IN_MILLISECOND).toInt())
|
||||
setDurationMillis((item.runTimeTicks / TICKS_IN_MILLISECOND).toInt())
|
||||
}
|
||||
// First episode of the season
|
||||
item.indexNumber == 0 -> {
|
||||
setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_NEW)
|
||||
}
|
||||
// Default
|
||||
else -> {
|
||||
setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_NEXT)
|
||||
}
|
||||
}
|
||||
|
||||
// Set intent to open the episode
|
||||
setIntent(Intent(application, StartupActivity::class.java).apply {
|
||||
putExtra("ItemId", item.id)
|
||||
})
|
||||
}.build()
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.jellyfin.androidtv.constants;
|
||||
|
||||
public class CodecTypes {
|
||||
/* Audio Codec Types */
|
||||
public static final String AAC = "aac";
|
||||
public static final String AAC_LATM = "aac_latm";
|
||||
public static final String AC3 = "ac3";
|
||||
public static final String APE = "ape";
|
||||
public static final String DCA = "dca";
|
||||
public static final String DTS = "dts";
|
||||
public static final String EAC3 = "eac3";
|
||||
public static final String FLAC = "flac";
|
||||
public static final String MP2 = "mp2";
|
||||
public static final String MP3 = "mp3";
|
||||
public static final String MPA = "mpa";
|
||||
public static final String OPUS = "opus";
|
||||
public static final String PCM = "pcm";
|
||||
public static final String PCM_S16LE = "PCM_S16LE";
|
||||
public static final String PCM_S24LE = "PCM_S24LE";
|
||||
public static final String TRUEHD = "truehd";
|
||||
public static final String VORBIS = "vorbis";
|
||||
public static final String WAV = "wav";
|
||||
public static final String WMA = "wma";
|
||||
public static final String WMAV2 = "wmav2";
|
||||
|
||||
/* Video Codec Types */
|
||||
public static final String AVC = "avc";
|
||||
public static final String H264 = "h264";
|
||||
public static final String H265 = "h265";
|
||||
public static final String HEVC = "hevc";
|
||||
public static final String MPEG2 = "mpeg2";
|
||||
public static final String MPEG2VIDEO = "mpeg2video";
|
||||
public static final String MPEG4 = "mpeg4";
|
||||
public static final String VP8 = "vp8";
|
||||
public static final String VP9 = "vp9";
|
||||
public static final String VPX = "vpx";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.jellyfin.androidtv.constants;
|
||||
|
||||
public class ContainerTypes {
|
||||
public static final String _3GP = "3gp";
|
||||
public static final String _3GPP = "3gpp";
|
||||
public static final String ASF = "asf";
|
||||
public static final String AVI = "avi";
|
||||
public static final String DVR_MS = "dvr-ms";
|
||||
public static final String M2TS = "m2ts";
|
||||
public static final String M2V = "m2v";
|
||||
public static final String M4V = "m4v";
|
||||
public static final String MKV = "mkv";
|
||||
public static final String MOV = "mov";
|
||||
public static final String MP4 = "mp4";
|
||||
public static final String MPEG = "mpeg";
|
||||
public static final String MPEGTS = "mpegts";
|
||||
public static final String MPG = "mpg";
|
||||
public static final String OGA = "oga";
|
||||
public static final String OGG = "ogg";
|
||||
public static final String OGM = "ogm";
|
||||
public static final String OGV = "ogv";
|
||||
public static final String TS = "ts";
|
||||
public static final String VOB = "vob";
|
||||
public static final String WEBM = "webm";
|
||||
public static final String WEBMA = "webma";
|
||||
public static final String WMV = "wmv";
|
||||
public static final String WTV = "wtv";
|
||||
public static final String XVID = "xvid";
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.jellyfin.androidtv.constants;
|
||||
|
||||
// Names used in jellyfin-web
|
||||
// Only uses those that are configurable in web settings
|
||||
public enum HomeSectionType {
|
||||
LATEST_MEDIA("latestmedia"),
|
||||
LIBRARY_TILES_SMALL("smalllibrarytiles"),
|
||||
LIBRARY_BUTTONS("librarybuttons"),
|
||||
RESUME("resume"),
|
||||
RESUME_AUDIO("resumeaudio"),
|
||||
ACTIVE_RECORDINGS("activerecordings"),
|
||||
NEXT_UP("nextup"),
|
||||
LIVE_TV("livetv"),
|
||||
|
||||
NONE("none");
|
||||
|
||||
public static HomeSectionType getByName(String name) {
|
||||
for (HomeSectionType type : HomeSectionType.values()) {
|
||||
if (type.name.equalsIgnoreCase(name)) return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public final String name;
|
||||
|
||||
HomeSectionType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.jellyfin.androidtv.constants;
|
||||
|
||||
public class MediaTypes {
|
||||
public static final String HLS = "hls";
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.jellyfin.androidtv.details;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.app.Activity;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
|
||||
public class ExpandedTextActivity extends Activity {
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
public class ExpandedTextActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
@@ -11,17 +11,17 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.ClassPresenterSelector;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.ClassPresenterSelector;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
@@ -77,6 +77,7 @@ import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemPerson;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.ImageOptions;
|
||||
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
|
||||
import org.jellyfin.apiclient.model.dto.UserItemDataDto;
|
||||
@@ -117,7 +118,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
private Calendar mLastUpdated;
|
||||
private String mPrevItemId;
|
||||
|
||||
private RowsFragment mRowsFragment;
|
||||
private RowsSupportFragment mRowsFragment;
|
||||
private ArrayObjectAdapter mRowsAdapter;
|
||||
|
||||
private MyDetailsOverviewRowPresenter mDorPresenter;
|
||||
@@ -145,14 +146,13 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
mActivity = this;
|
||||
BackgroundManager backgroundManager = BackgroundManager.getInstance(this);
|
||||
backgroundManager.attach(getWindow());
|
||||
backgroundManager.setDimLayer(getDrawable(R.drawable.left_fade));
|
||||
|
||||
mBackgroundTarget = new MyPicassoBackgroundManagerTarget(backgroundManager);
|
||||
mMetrics = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
|
||||
|
||||
mRowsFragment = new RowsFragment();
|
||||
getFragmentManager().beginTransaction().add(R.id.rowsFragment, mRowsFragment).commit();
|
||||
mRowsFragment = new RowsSupportFragment();
|
||||
getSupportFragmentManager().beginTransaction().add(R.id.rowsFragment, mRowsFragment).commit();
|
||||
|
||||
mRowsFragment.setOnItemViewClickedListener(new ItemViewClickedListener());
|
||||
mRowsFragment.setOnItemViewSelectedListener(new ItemViewSelectedListener());
|
||||
@@ -169,7 +169,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
registerMessageListener(new IMessageListener() {
|
||||
@Override
|
||||
public void onMessageReceived(CustomMessage message) {
|
||||
if (message == CustomMessage.ActionComplete && mSeriesTimerInfo != null && "SeriesTimer".equals(mBaseItem.getType())) {
|
||||
if (message == CustomMessage.ActionComplete && mSeriesTimerInfo != null && mBaseItem.getBaseItemType() == BaseItemType.SeriesTimer) {
|
||||
//update info
|
||||
mApplication.getApiClient().GetLiveTvSeriesTimerAsync(mSeriesTimerInfo.getId(), new Response<SeriesTimerInfoDto>() {
|
||||
@Override
|
||||
@@ -206,11 +206,11 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
rotateBackdrops();
|
||||
|
||||
//Update information that may have changed - delay slightly to allow changes to take on the server
|
||||
if (mApplication.getLastPlayback().after(mLastUpdated) && !"MusicArtist".equals(mBaseItem.getType())) {
|
||||
if (mApplication.getLastPlayback().after(mLastUpdated) && mBaseItem.getBaseItemType() != BaseItemType.MusicArtist) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ("Episode".equals(mBaseItem.getType()) && mApplication.getLastPlayback().after(mLastUpdated) && mApplication.getLastPlayedItem() != null && !mBaseItem.getId().equals(mApplication.getLastPlayedItem().getId()) && "Episode".equals(mApplication.getLastPlayedItem().getType())) {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Episode && mApplication.getLastPlayback().after(mLastUpdated) && mApplication.getLastPlayedItem() != null && !mBaseItem.getId().equals(mApplication.getLastPlayedItem().getId()) && mApplication.getLastPlayedItem().getBaseItemType() == BaseItemType.Episode) {
|
||||
mApplication.getLogger().Info("Re-loading after new episode playback");
|
||||
loadItem(mApplication.getLastPlayedItem().getId());
|
||||
mApplication.setLastPlayedItem(null); //blank this out so a detail screen we back up to doesn't also do this
|
||||
@@ -222,7 +222,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
if (!isFinishing()) {
|
||||
mBaseItem = response;
|
||||
if (mResumeButton != null) {
|
||||
mResumeButton.setVisibility(("Series".equals(mBaseItem.getType()) && ! mBaseItem.getUserData().getPlayed()) || response.getCanResume() ? View.VISIBLE : View.GONE);
|
||||
mResumeButton.setVisibility((mBaseItem.getBaseItemType() == BaseItemType.Series && ! mBaseItem.getUserData().getPlayed()) || response.getCanResume() ? View.VISIBLE : View.GONE);
|
||||
if (response.getCanResume()){
|
||||
mResumeButton.setText(String.format(getString(R.string.lbl_resume_from), TimeUtils.formatMillis((response.getUserData().getPlaybackPositionTicks()/10000) - mApplication.getResumePreroll())));
|
||||
}
|
||||
@@ -263,7 +263,6 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (mCurrentItem != null) {
|
||||
return KeyProcessor.HandleKey(keyCode, mCurrentItem, this) || super.onKeyUp(keyCode, event);
|
||||
|
||||
} else if ((keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) && BaseItemUtils.canPlay(mBaseItem)) {
|
||||
//default play action
|
||||
Long pos = mBaseItem.getUserData().getPlaybackPositionTicks() / 10000;
|
||||
@@ -294,8 +293,25 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] buttonTypes = new String[] {"Episode","Movie","Series","Season","Folder","Video","Recording","Program","ChannelVideoItem","Trailer","MusicArtist","Person","MusicVideo","SeriesTimer"};
|
||||
private static List<String> buttonTypeList = Arrays.asList(buttonTypes);
|
||||
private static BaseItemType[] buttonTypes = new BaseItemType[] {
|
||||
BaseItemType.Episode,
|
||||
BaseItemType.Movie,
|
||||
BaseItemType.Series,
|
||||
BaseItemType.Season,
|
||||
BaseItemType.Folder,
|
||||
BaseItemType.Video,
|
||||
BaseItemType.Recording,
|
||||
BaseItemType.Program,
|
||||
BaseItemType.ChannelVideoItem,
|
||||
BaseItemType.Trailer,
|
||||
BaseItemType.MusicArtist,
|
||||
BaseItemType.Person,
|
||||
BaseItemType.MusicVideo,
|
||||
BaseItemType.SeriesTimer
|
||||
};
|
||||
|
||||
|
||||
private static List<BaseItemType> buttonTypeList = Arrays.asList(buttonTypes);
|
||||
private static String[] directPlayableTypes = new String[] {"Episode","Movie","Video","Recording","Program"};
|
||||
private static List<String> directPlayableTypeList = Arrays.asList(directPlayableTypes);
|
||||
|
||||
@@ -311,7 +327,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
private void updateWatched() {
|
||||
if (mWatchedToggleButton != null && mBaseItem != null && mBaseItem.getUserData() != null && !isFinishing()) {
|
||||
mWatchedToggleButton.setImageResource(mBaseItem.getUserData().getPlayed() ? R.drawable.redcheck : R.drawable.whitecheck);
|
||||
mWatchedToggleButton.setImageResource(mBaseItem.getUserData().getPlayed() ? R.drawable.ic_watch_red : R.drawable.ic_watch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +349,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
BaseItemDto item = new BaseItemDto();
|
||||
item.setId(mSeriesTimerInfo.getId());
|
||||
item.setSeriesTimerId(mSeriesTimerInfo.getId());
|
||||
item.setType("SeriesTimer");
|
||||
item.setBaseItemType(BaseItemType.SeriesTimer);
|
||||
item.setName(mSeriesTimerInfo.getName());
|
||||
item.setOverview(BaseItemUtils.getSeriesOverview(mSeriesTimerInfo));
|
||||
|
||||
@@ -348,7 +364,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
@Override
|
||||
public void setRecTimer(String id) {
|
||||
mProgramInfo.setTimerId(id);
|
||||
if (mRecordButton != null) mRecordButton.setImageResource(id == null ? R.drawable.recwhite : R.drawable.rec);
|
||||
if (mRecordButton != null) mRecordButton.setImageResource(id == null ? R.drawable.ic_record : R.drawable.ic_record_red);
|
||||
}
|
||||
|
||||
private int posterWidth;
|
||||
@@ -357,7 +373,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
@Override
|
||||
public void setRecSeriesTimer(String id) {
|
||||
if (mProgramInfo != null) mProgramInfo.setSeriesTimerId(id);
|
||||
if (mRecSeriesButton != null) mRecSeriesButton.setImageResource(id == null ? R.drawable.recserieswhite : R.drawable.recseries);
|
||||
if (mRecSeriesButton != null) mRecSeriesButton.setImageResource(id == null ? R.drawable.ic_record_series : R.drawable.ic_record_series_red);
|
||||
if (mSeriesSettingsButton != null) mSeriesSettingsButton.setVisibility(id == null ? View.GONE : View.VISIBLE);
|
||||
|
||||
}
|
||||
@@ -370,7 +386,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
// Figure image size
|
||||
Double aspect = ImageUtils.getImageAspectRatio(item, false);
|
||||
posterHeight = aspect > 1 ? Utils.convertDpToPixel(mActivity, 160) : Utils.convertDpToPixel(mActivity, "Person".equals(item.getType()) || "MusicArtist".equals(item.getType()) ? 300 : 200);
|
||||
posterHeight = aspect > 1 ? Utils.convertDpToPixel(mActivity, 160) : Utils.convertDpToPixel(mActivity, item.getBaseItemType() == BaseItemType.Person || item.getBaseItemType() == BaseItemType.MusicArtist ? 300 : 200);
|
||||
posterWidth = (int)((aspect) * posterHeight);
|
||||
if (posterHeight < 10) posterWidth = Utils.convertDpToPixel(mActivity, 150); //Guard against zero size images causing picasso to barf
|
||||
|
||||
@@ -379,9 +395,9 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
mDetailsOverviewRow = new MyDetailsOverviewRow(item);
|
||||
|
||||
mDetailsOverviewRow.setSummary(item.getOverview());
|
||||
switch (item.getType()) {
|
||||
case "Person":
|
||||
case "MusicArtist":
|
||||
switch (item.getBaseItemType()) {
|
||||
case Person:
|
||||
case MusicArtist:
|
||||
mDetailsOverviewRow.setSummarySubTitle("");
|
||||
break;
|
||||
default:
|
||||
@@ -389,7 +405,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
BaseItemPerson director = BaseItemUtils.getFirstPerson(item, PersonType.Director);
|
||||
|
||||
InfoItem firstRow;
|
||||
if ("Series".equals(item.getType())) {
|
||||
if (item.getBaseItemType() == BaseItemType.Series) {
|
||||
firstRow = new InfoItem(
|
||||
getString(R.string.lbl_seasons),
|
||||
Utils.getSafeValue(item.getChildCount(), 0).toString());
|
||||
@@ -489,9 +505,9 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
protected void addAdditionalRows(ArrayObjectAdapter adapter) {
|
||||
TvApp.getApplication().getLogger().Debug("Item type: "+mBaseItem.getType());
|
||||
switch (mBaseItem.getType()) {
|
||||
case "Movie":
|
||||
TvApp.getApplication().getLogger().Debug("Item type: " + mBaseItem.getBaseItemType());
|
||||
switch (mBaseItem.getBaseItemType()) {
|
||||
case Movie:
|
||||
|
||||
//Cast/Crew
|
||||
if (mBaseItem.getPeople() != null && mBaseItem.getPeople().length > 0) {
|
||||
@@ -528,7 +544,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
addInfoRows(adapter);
|
||||
break;
|
||||
case "Trailer":
|
||||
case Trailer:
|
||||
|
||||
//Cast/Crew
|
||||
if (mBaseItem.getPeople() != null && mBaseItem.getPeople().length > 0) {
|
||||
@@ -547,7 +563,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
addItemRow(adapter, similarTrailerAdapter, 4, getString(R.string.lbl_more_like_this));
|
||||
addInfoRows(adapter);
|
||||
break;
|
||||
case "Person":
|
||||
case Person:
|
||||
|
||||
ItemQuery personMovies = new ItemQuery();
|
||||
personMovies.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio});
|
||||
@@ -568,7 +584,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
addItemRow(adapter, personSeriesAdapter, 1, mApplication.getString(R.string.lbl_tv_series));
|
||||
|
||||
break;
|
||||
case "MusicArtist":
|
||||
case MusicArtist:
|
||||
|
||||
ItemQuery artistAlbums = new ItemQuery();
|
||||
artistAlbums.setFields(new ItemFields[]{ItemFields.PrimaryImageAspectRatio});
|
||||
@@ -580,7 +596,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
addItemRow(adapter, artistAlbumsAdapter, 0, mApplication.getString(R.string.lbl_albums));
|
||||
|
||||
break;
|
||||
case "Series":
|
||||
case Series:
|
||||
NextUpQuery nextUpQuery = new NextUpQuery();
|
||||
nextUpQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
nextUpQuery.setSeriesId(mBaseItem.getId());
|
||||
@@ -617,7 +633,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
addItemRow(adapter, similarAdapter, 4, getString(R.string.lbl_more_like_this));
|
||||
break;
|
||||
|
||||
case "Episode":
|
||||
case Episode:
|
||||
if (mBaseItem.getSeasonId() != null && mBaseItem.getIndexNumber() != null) {
|
||||
StdItemQuery nextEpisodes = new StdItemQuery();
|
||||
nextEpisodes.setParentId(mBaseItem.getSeasonId());
|
||||
@@ -650,7 +666,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
addInfoRows(adapter);
|
||||
break;
|
||||
|
||||
case "SeriesTimer":
|
||||
case SeriesTimer:
|
||||
TimerQuery scheduled = new TimerQuery();
|
||||
scheduled.setSeriesTimerId(mSeriesTimerInfo.getId());
|
||||
TvManager.getScheduleRowsAsync(scheduled, new CardPresenter(true), adapter, new Response<Integer>());
|
||||
@@ -678,7 +694,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
private void updateInfo(BaseItemDto item) {
|
||||
if (buttonTypeList.contains(item.getType())) addButtons(BUTTON_SIZE);
|
||||
if (buttonTypeList.contains(item.getBaseItemType())) addButtons(BUTTON_SIZE);
|
||||
// updatePlayedDate();
|
||||
//
|
||||
updateBackground(ImageUtils.getBackdropImageUrl(item, TvApp.getApplication().getApiClient(), true));
|
||||
@@ -707,10 +723,10 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
private String getEndTime() {
|
||||
if (mBaseItem != null && !"MusicArtist".equals(mBaseItem.getType()) && !"Person".equals(mBaseItem.getType())) {
|
||||
if (mBaseItem != null && mBaseItem.getBaseItemType() != BaseItemType.MusicArtist && mBaseItem.getBaseItemType() != BaseItemType.Person) {
|
||||
Long runtime = Utils.getSafeValue(mBaseItem.getRunTimeTicks(), mBaseItem.getOriginalRunTimeTicks());
|
||||
if (runtime != null && runtime > 0) {
|
||||
long endTimeTicks = "Program".equals(mBaseItem.getType()) && mBaseItem.getEndDate() != null ? TimeUtils.convertToLocalDate(mBaseItem.getEndDate()).getTime() : System.currentTimeMillis() + runtime / 10000;
|
||||
long endTimeTicks = mBaseItem.getBaseItemType() == BaseItemType.Program && mBaseItem.getEndDate() != null ? TimeUtils.convertToLocalDate(mBaseItem.getEndDate()).getTime() : System.currentTimeMillis() + runtime / 10000;
|
||||
if (mBaseItem.getCanResume()) {
|
||||
endTimeTicks = System.currentTimeMillis() + ((runtime - mBaseItem.getUserData().getPlaybackPositionTicks()) / 10000);
|
||||
return android.text.format.DateFormat.getTimeFormat(this).format(new Date(endTimeTicks));
|
||||
@@ -724,7 +740,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
private void addItemToQueue() {
|
||||
if ("Audio".equals(mBaseItem.getType())) {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Audio) {
|
||||
MediaManager.addToAudioQueue(Arrays.asList(mBaseItem));
|
||||
|
||||
} else {
|
||||
@@ -738,7 +754,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
@Override
|
||||
public void onResponse(UserItemDataDto response) {
|
||||
mBaseItem.setUserData(response);
|
||||
favButton.setImageResource(response.getIsFavorite() ? R.drawable.redheart : R.drawable.whiteheart);
|
||||
favButton.setImageResource(response.getIsFavorite() ? R.drawable.ic_heart_red : R.drawable.ic_heart);
|
||||
TvApp.getApplication().setLastFavoriteUpdate(System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
@@ -792,10 +808,20 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
private TextUnderButton moreButton;
|
||||
|
||||
private void addButtons(int buttonSize) {
|
||||
mResumeButton = new TextUnderButton(this, R.drawable.resume, buttonSize, 2, "Series".equals(mBaseItem.getType()) ? getString(R.string.lbl_play_next_up) : String.format(getString(R.string.lbl_resume_from), TimeUtils.formatMillis((mBaseItem.getUserData().getPlaybackPositionTicks()/10000) - mApplication.getResumePreroll())), new View.OnClickListener() {
|
||||
String buttonLabel;
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Series || mBaseItem.getBaseItemType() == BaseItemType.SeriesTimer) {
|
||||
buttonLabel = getString(R.string.lbl_play_next_up);
|
||||
} else {
|
||||
long startPos = 0;
|
||||
if (mBaseItem.getCanResume()) {
|
||||
startPos = (mBaseItem.getUserData().getPlaybackPositionTicks()/10000) - mApplication.getResumePreroll();
|
||||
}
|
||||
buttonLabel = String.format(getString(R.string.lbl_resume_from), TimeUtils.formatMillis(startPos));
|
||||
}
|
||||
mResumeButton = new TextUnderButton(this, R.drawable.ic_resume, buttonSize, 2, buttonLabel, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if ("Series".equals(mBaseItem.getType())) {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Series) {
|
||||
//play next up
|
||||
NextUpQuery nextUpQuery = new NextUpQuery();
|
||||
nextUpQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
@@ -827,18 +853,24 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
if (BaseItemUtils.canPlay(mBaseItem)) {
|
||||
mDetailsOverviewRow.addAction(mResumeButton);
|
||||
mResumeButton.setVisibility(("Series".equals(mBaseItem.getType()) && ! mBaseItem.getUserData().getPlayed()) || (mBaseItem.getCanResume() ) ? View.VISIBLE : View.GONE);
|
||||
boolean resumeButtonVisible = (mBaseItem.getBaseItemType() == BaseItemType.Series && !mBaseItem.getUserData().getPlayed()) || (mBaseItem.getCanResume());
|
||||
mResumeButton.setVisibility(resumeButtonVisible ? View.VISIBLE : View.GONE);
|
||||
|
||||
TextUnderButton play = new TextUnderButton(this, R.drawable.play, buttonSize, 2, getString(BaseItemUtils.isLiveTv(mBaseItem) ? R.string.lbl_tune_to_channel : mBaseItem.getIsFolderItem() ? R.string.lbl_play_all : R.string.lbl_play), new View.OnClickListener() {
|
||||
TextUnderButton play = new TextUnderButton(this, R.drawable.ic_play, buttonSize, 2, getString(BaseItemUtils.isLiveTv(mBaseItem) ? R.string.lbl_tune_to_channel : mBaseItem.getIsFolderItem() ? R.string.lbl_play_all : R.string.lbl_play), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
play(mBaseItem, 0, false);
|
||||
}
|
||||
});
|
||||
mDetailsOverviewRow.addAction(play);
|
||||
if (resumeButtonVisible) {
|
||||
mResumeButton.requestFocus();
|
||||
} else {
|
||||
play.requestFocus();
|
||||
}
|
||||
|
||||
if (!mBaseItem.getIsFolderItem() && !BaseItemUtils.isLiveTv(mBaseItem)) {
|
||||
queueButton = new TextUnderButton(this, R.drawable.addtoqueue, buttonSize, 2, getString(R.string.lbl_add_to_queue), new View.OnClickListener() {
|
||||
queueButton = new TextUnderButton(this, R.drawable.ic_add, buttonSize, 2, getString(R.string.lbl_add_to_queue), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
addItemToQueue();
|
||||
@@ -848,7 +880,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
if (mBaseItem.getIsFolderItem()) {
|
||||
TextUnderButton shuffle = new TextUnderButton(this, R.drawable.shuffle, buttonSize, 2, getString(R.string.lbl_shuffle_all), new View.OnClickListener() {
|
||||
TextUnderButton shuffle = new TextUnderButton(this, R.drawable.ic_shuffle, buttonSize, 2, getString(R.string.lbl_shuffle_all), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
play(mBaseItem, 0, true);
|
||||
@@ -857,8 +889,8 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
mDetailsOverviewRow.addAction(shuffle);
|
||||
}
|
||||
|
||||
if ("MusicArtist".equals(mBaseItem.getType())) {
|
||||
TextUnderButton imix = new TextUnderButton(this, R.drawable.mix, buttonSize, getString(R.string.lbl_instant_mix), new View.OnClickListener() {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.MusicArtist) {
|
||||
TextUnderButton imix = new TextUnderButton(this, R.drawable.ic_mix, buttonSize, getString(R.string.lbl_instant_mix), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Utils.beep();
|
||||
@@ -871,7 +903,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
if (mBaseItem.getLocalTrailerCount() != null && mBaseItem.getLocalTrailerCount() > 0) {
|
||||
TextUnderButton trailer = new TextUnderButton(this, R.drawable.trailer, buttonSize, getString(R.string.lbl_play_trailers), new View.OnClickListener() {
|
||||
TextUnderButton trailer = new TextUnderButton(this, R.drawable.ic_trailer, buttonSize, getString(R.string.lbl_play_trailers), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TvApp.getApplication().getApiClient().GetLocalTrailersAsync(TvApp.getApplication().getCurrentUser().getId(), mBaseItem.getId(), new Response<BaseItemDto[]>() {
|
||||
@@ -896,7 +928,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
if (mProgramInfo != null && mApplication.canManageRecordings()) {
|
||||
if (TimeUtils.convertToLocalDate(mBaseItem.getEndDate()).getTime() > System.currentTimeMillis()) {
|
||||
//Record button
|
||||
mRecordButton = new TextUnderButton(this, mProgramInfo.getTimerId() != null ? R.drawable.rec : R.drawable.recwhite, buttonSize, 4, getString(R.string.lbl_record), new View.OnClickListener() {
|
||||
mRecordButton = new TextUnderButton(this, mProgramInfo.getTimerId() != null ? R.drawable.ic_record_red : R.drawable.ic_record, buttonSize, 4, getString(R.string.lbl_record), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mProgramInfo.getTimerId() == null) {
|
||||
@@ -957,7 +989,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
|
||||
if (mProgramInfo.getIsSeries() != null && mProgramInfo.getIsSeries()) {
|
||||
mRecSeriesButton= new TextUnderButton(this, mProgramInfo.getSeriesTimerId() != null ? R.drawable.recseries : R.drawable.recserieswhite, buttonSize, 4, getString(R.string.lbl_record_series), new View.OnClickListener() {
|
||||
mRecSeriesButton= new TextUnderButton(this, mProgramInfo.getSeriesTimerId() != null ? R.drawable.ic_record_series_red : R.drawable.ic_record_series, buttonSize, 4, getString(R.string.lbl_record_series), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mProgramInfo.getSeriesTimerId() == null) {
|
||||
@@ -1026,7 +1058,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
mDetailsOverviewRow.addAction(mRecSeriesButton);
|
||||
|
||||
mSeriesSettingsButton = new TextUnderButton(this, R.drawable.cog, buttonSize, 2, getString(R.string.lbl_series_settings), new View.OnClickListener() {
|
||||
mSeriesSettingsButton = new TextUnderButton(this, R.drawable.ic_settings, buttonSize, 2, getString(R.string.lbl_series_settings), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showRecordingOptions(mProgramInfo.getSeriesTimerId(), mProgramInfo, true);
|
||||
@@ -1041,13 +1073,13 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
UserItemDataDto userData = mBaseItem.getUserData();
|
||||
if (userData != null && mProgramInfo == null) {
|
||||
if (!"MusicArtist".equals(mBaseItem.getType()) && !"Person".equals(mBaseItem.getType())) {
|
||||
mWatchedToggleButton = new TextUnderButton(this, userData.getPlayed() ? R.drawable.redcheck : R.drawable.whitecheck, buttonSize, getString(R.string.lbl_toggle_watched), markWatchedListener);
|
||||
if (mBaseItem.getBaseItemType() != BaseItemType.MusicArtist && mBaseItem.getBaseItemType() != BaseItemType.Person) {
|
||||
mWatchedToggleButton = new TextUnderButton(this, userData.getPlayed() ? R.drawable.ic_watch_red : R.drawable.ic_watch, buttonSize, getString(R.string.lbl_watched), markWatchedListener);
|
||||
mDetailsOverviewRow.addAction(mWatchedToggleButton);
|
||||
}
|
||||
|
||||
//Favorite
|
||||
favButton = new TextUnderButton(this, userData.getIsFavorite() ? R.drawable.redheart : R.drawable.whiteheart, buttonSize, 2, getString(R.string.lbl_toggle_favorite), new View.OnClickListener() {
|
||||
favButton = new TextUnderButton(this, userData.getIsFavorite() ? R.drawable.ic_heart_red : R.drawable.ic_heart, buttonSize, 2, getString(R.string.lbl_favorite), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
toggleFavorite();
|
||||
@@ -1056,9 +1088,9 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
mDetailsOverviewRow.addAction(favButton);
|
||||
}
|
||||
|
||||
if ("Episode".equals(mBaseItem.getType()) && mBaseItem.getSeriesId() != null) {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Episode && mBaseItem.getSeriesId() != null) {
|
||||
//add the prev button first so it will be there in proper position - we'll show it later if needed
|
||||
mPrevButton = new TextUnderButton(this, R.drawable.prev, buttonSize, 3, getString(R.string.lbl_previous_episode), new View.OnClickListener() {
|
||||
mPrevButton = new TextUnderButton(this, R.drawable.ic_previous_episode, buttonSize, 3, getString(R.string.lbl_previous_episode), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPrevItemId != null) {
|
||||
@@ -1092,7 +1124,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
}
|
||||
});
|
||||
|
||||
goToSeriesButton = new TextUnderButton(this, R.drawable.tvicon, buttonSize, getString(R.string.lbl_goto_series), new View.OnClickListener() {
|
||||
goToSeriesButton = new TextUnderButton(this, R.drawable.ic_tv, buttonSize, getString(R.string.lbl_goto_series), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
gotoSeries();
|
||||
@@ -1101,9 +1133,9 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
mDetailsOverviewRow.addAction(goToSeriesButton);
|
||||
}
|
||||
|
||||
if (("Recording".equals(mBaseItem.getType()) && TvApp.getApplication().getCurrentUser().getPolicy().getEnableLiveTvManagement() && mBaseItem.getCanDelete()) ||
|
||||
(("Movie".equals(mBaseItem.getType()) || "Episode".equals(mBaseItem.getType()) || "Video".equals(mBaseItem.getType())) && TvApp.getApplication().getCurrentUser().getPolicy().getEnableContentDeletion())) {
|
||||
deleteButton = new TextUnderButton(this, R.drawable.trash, buttonSize, getString(R.string.lbl_delete), new View.OnClickListener() {
|
||||
if ((mBaseItem.getBaseItemType() == BaseItemType.Recording && TvApp.getApplication().getCurrentUser().getPolicy().getEnableLiveTvManagement() && mBaseItem.getCanDelete()) ||
|
||||
((mBaseItem.getBaseItemType() == BaseItemType.Movie || mBaseItem.getBaseItemType() == BaseItemType.Episode || mBaseItem.getBaseItemType() == BaseItemType.Video) && TvApp.getApplication().getCurrentUser().getPolicy().getEnableContentDeletion())) {
|
||||
deleteButton = new TextUnderButton(this, R.drawable.ic_trash, buttonSize, getString(R.string.lbl_delete), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
deleteItem();
|
||||
@@ -1112,9 +1144,9 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
mDetailsOverviewRow.addAction(deleteButton);
|
||||
}
|
||||
|
||||
if (mSeriesTimerInfo != null && "SeriesTimer".equals(mBaseItem.getType())) {
|
||||
if (mSeriesTimerInfo != null && mBaseItem.getBaseItemType() == BaseItemType.SeriesTimer) {
|
||||
//Settings
|
||||
mDetailsOverviewRow.addAction(new TextUnderButton(this, R.drawable.cog, buttonSize, getString(R.string.lbl_series_settings), new View.OnClickListener() {
|
||||
mDetailsOverviewRow.addAction(new TextUnderButton(this, R.drawable.ic_settings, buttonSize, getString(R.string.lbl_series_settings), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
//show recording options
|
||||
@@ -1124,7 +1156,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
//Delete
|
||||
final Activity activity = this;
|
||||
TextUnderButton del = new TextUnderButton(this, R.drawable.trash, buttonSize, getString(R.string.lbl_cancel_series), new View.OnClickListener() {
|
||||
TextUnderButton del = new TextUnderButton(this, R.drawable.ic_trash, buttonSize, getString(R.string.lbl_cancel_series), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new AlertDialog.Builder(activity)
|
||||
@@ -1198,7 +1230,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
moreButton.setVisibility(View.GONE);
|
||||
mDetailsOverviewRow.addAction(moreButton);
|
||||
if (!"Episode".equals(mBaseItem.getType())) showMoreButtonIfNeeded(); //Episodes check for previous and then call this above
|
||||
if (mBaseItem.getBaseItemType() != BaseItemType.Episode) showMoreButtonIfNeeded(); //Episodes check for previous and then call this above
|
||||
|
||||
}
|
||||
|
||||
@@ -1361,7 +1393,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
@Override
|
||||
public void onResponse(UserItemDataDto response) {
|
||||
mBaseItem.setUserData(response);
|
||||
mWatchedToggleButton.setImageResource(R.drawable.redcheck);
|
||||
mWatchedToggleButton.setImageResource(R.drawable.ic_watch_red);
|
||||
//adjust resume
|
||||
if (mResumeButton != null && !mBaseItem.getCanResume())
|
||||
mResumeButton.setVisibility(View.GONE);
|
||||
@@ -1378,7 +1410,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
@Override
|
||||
public void onResponse(UserItemDataDto response) {
|
||||
mBaseItem.setUserData(response);
|
||||
mWatchedToggleButton.setImageResource(R.drawable.whitecheck);
|
||||
mWatchedToggleButton.setImageResource(R.drawable.ic_watch);
|
||||
//adjust resume
|
||||
if (mResumeButton != null && !mBaseItem.getCanResume())
|
||||
mResumeButton.setVisibility(View.GONE);
|
||||
@@ -1392,13 +1424,13 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
protected void play(final BaseItemDto item, final int pos, final boolean shuffle) {
|
||||
final Activity activity = this;
|
||||
PlaybackHelper.getItemsToPlay(item, pos == 0 && item.getType().equals("Movie"), shuffle, new Response<List<BaseItemDto>>() {
|
||||
PlaybackHelper.getItemsToPlay(item, pos == 0 && item.getBaseItemType() == BaseItemType.Movie, shuffle, new Response<List<BaseItemDto>>() {
|
||||
@Override
|
||||
public void onResponse(List<BaseItemDto> response) {
|
||||
if ("MusicArtist".equals(item.getType())) {
|
||||
if (item.getBaseItemType() == BaseItemType.MusicArtist) {
|
||||
MediaManager.playNow(response);
|
||||
} else {
|
||||
Intent intent = new Intent(activity, mApplication.getPlaybackActivityClass(item.getType()));
|
||||
Intent intent = new Intent(activity, mApplication.getPlaybackActivityClass(item.getBaseItemType()));
|
||||
MediaManager.setCurrentVideoQueue(response);
|
||||
intent.putExtra("Position", pos);
|
||||
startActivity(intent);
|
||||
@@ -1410,7 +1442,7 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic
|
||||
|
||||
protected void play(final BaseItemDto[] items, final int pos, final boolean shuffle) {
|
||||
List<BaseItemDto> itemsToPlay = Arrays.asList(items);
|
||||
Intent intent = new Intent(this, mApplication.getPlaybackActivityClass(items[0].getType()));
|
||||
Intent intent = new Intent(this, mApplication.getPlaybackActivityClass(items[0].getBaseItemType()));
|
||||
if (shuffle) Collections.shuffle(itemsToPlay);
|
||||
MediaManager.setCurrentVideoQueue(itemsToPlay);
|
||||
intent.putExtra("Position", pos);
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
@@ -57,6 +57,7 @@ import java.util.List;
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.UserItemDataDto;
|
||||
import org.jellyfin.apiclient.model.library.PlayAccess;
|
||||
import org.jellyfin.apiclient.model.playlists.PlaylistItemQuery;
|
||||
@@ -153,7 +154,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
mItemList.setRowClickedListener(new ItemRowView.RowClickedListener() {
|
||||
@Override
|
||||
public void onRowClicked(ItemRowView row) {
|
||||
showMenu(row, !"Audio".equals(row.getItem().getType()));
|
||||
showMenu(row, row.getItem().getBaseItemType() != BaseItemType.Audio);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -194,7 +195,6 @@ public class ItemListActivity extends BaseActivity {
|
||||
|
||||
BackgroundManager backgroundManager = BackgroundManager.getInstance(this);
|
||||
backgroundManager.attach(getWindow());
|
||||
backgroundManager.setDimLayer(getDrawable(R.drawable.left_fade));
|
||||
mBackgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
|
||||
|
||||
mItemId = getIntent().getStringExtra("ItemId");
|
||||
@@ -331,7 +331,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if ("Audio".equals(row.getItem().getType())) {
|
||||
if (row.getItem().getBaseItemType() == BaseItemType.Audio) {
|
||||
MenuItem mix = menu.getMenu().add(0, 1, order++, R.string.lbl_instant_mix);
|
||||
mix.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
@@ -357,7 +357,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
item.setOverview(getString(R.string.desc_automatic_fav_songs));
|
||||
item.setPlayAccess(PlayAccess.Full);
|
||||
item.setMediaType("Audio");
|
||||
item.setType("Playlist");
|
||||
item.setBaseItemType(BaseItemType.Playlist);
|
||||
item.setIsFolder(true);
|
||||
setBaseItem(item);
|
||||
break;
|
||||
@@ -368,7 +368,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
queue.setOverview(getString(R.string.desc_current_video_queue));
|
||||
queue.setPlayAccess(PlayAccess.Full);
|
||||
queue.setMediaType("Video");
|
||||
queue.setType("Playlist");
|
||||
queue.setBaseItemType(BaseItemType.Playlist);
|
||||
queue.setIsFolder(true);
|
||||
if (MediaManager.getCurrentVideoQueue() != null) {
|
||||
long runtime = 0;
|
||||
@@ -406,7 +406,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
updatePoster(mBaseItem);
|
||||
|
||||
//get items
|
||||
if ("Playlist".equals(mBaseItem.getType())) {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Playlist) {
|
||||
// Have to use different query here
|
||||
switch (mItemId) {
|
||||
case FAV_SONGS:
|
||||
@@ -486,10 +486,10 @@ public class ItemListActivity extends BaseActivity {
|
||||
private void updatePoster(BaseItemDto item){
|
||||
switch (mItemId) {
|
||||
case FAV_SONGS:
|
||||
mPoster.setImageResource(R.drawable.genericmusic);
|
||||
mPoster.setImageResource(R.drawable.favorites);
|
||||
break;
|
||||
case VIDEO_QUEUE:
|
||||
mPoster.setImageResource(R.drawable.transplaylist);
|
||||
mPoster.setImageResource(R.drawable.ic_video_queue);
|
||||
break;
|
||||
default:
|
||||
// Figure image size
|
||||
@@ -516,7 +516,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
for (String genre : mBaseItem.getGenres()) {
|
||||
if (!first) InfoLayoutHelper.addSpacer(this, layout, " / ", 14);
|
||||
first = false;
|
||||
layout.addView(new GenreButton(this, roboto, 16, genre, mBaseItem.getType()));
|
||||
layout.addView(new GenreButton(this, roboto, 16, genre, mBaseItem.getBaseItemType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -541,7 +541,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
|
||||
private void play(List<BaseItemDto> items) {
|
||||
if ("Video".equals(mBaseItem.getMediaType())) {
|
||||
Intent intent = new Intent(mActivity, mApplication.getPlaybackActivityClass(mBaseItem.getType()));
|
||||
Intent intent = new Intent(mActivity, mApplication.getPlaybackActivityClass(mBaseItem.getBaseItemType()));
|
||||
//Resume first item if needed
|
||||
BaseItemDto first = items.size() > 0 ? items.get(0) : null;
|
||||
if (first != null && first.getUserData() != null) {
|
||||
@@ -560,7 +560,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
|
||||
private void addButtons(int buttonSize) {
|
||||
if (BaseItemUtils.canPlay(mBaseItem)) {
|
||||
TextUnderButton play = new TextUnderButton(this, R.drawable.play, buttonSize, 2, getString(mBaseItem.getIsFolderItem() ? R.string.lbl_play_all : R.string.lbl_play), new View.OnClickListener() {
|
||||
TextUnderButton play = new TextUnderButton(this, R.drawable.ic_play, buttonSize, 2, getString(mBaseItem.getIsFolderItem() ? R.string.lbl_play_all : R.string.lbl_play), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mItems.size() > 0) {
|
||||
@@ -574,11 +574,11 @@ public class ItemListActivity extends BaseActivity {
|
||||
mButtonRow.addView(play);
|
||||
play.requestFocus();
|
||||
if (mBaseItem.getIsFolderItem()) {
|
||||
TextUnderButton shuffle = new TextUnderButton(this, R.drawable.shuffle, buttonSize, 2, getString(R.string.lbl_shuffle_all), new View.OnClickListener() {
|
||||
TextUnderButton shuffle = new TextUnderButton(this, R.drawable.ic_shuffle, buttonSize, 2, getString(R.string.lbl_shuffle_all), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mItems.size() > 0) {
|
||||
if (mBaseItem.getId().equals(VIDEO_QUEUE) || mBaseItem.getId().equals(FAV_SONGS)) {
|
||||
if (mBaseItem.getId().equals(VIDEO_QUEUE) || mBaseItem.getId().equals(FAV_SONGS) || "Playlist".equals(mBaseItem.getType())) {
|
||||
List<BaseItemDto> shuffled = new ArrayList<>(mItems);
|
||||
Collections.shuffle(shuffled);
|
||||
play(shuffled);
|
||||
@@ -597,8 +597,8 @@ public class ItemListActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
if ("MusicAlbum".equals(mBaseItem.getType())) {
|
||||
TextUnderButton mix = new TextUnderButton(this, R.drawable.mix, buttonSize, 2, getString(R.string.lbl_instant_mix), new View.OnClickListener() {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.MusicAlbum) {
|
||||
TextUnderButton mix = new TextUnderButton(this, R.drawable.ic_mix, buttonSize, 2, getString(R.string.lbl_instant_mix), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
Utils.beep();
|
||||
@@ -612,7 +612,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
if (!mItemId.equals(FAV_SONGS)) {
|
||||
if (!mItemId.equals(VIDEO_QUEUE)) {
|
||||
//Favorite
|
||||
TextUnderButton fav = new TextUnderButton(this, mBaseItem.getUserData().getIsFavorite() ? R.drawable.redheart : R.drawable.whiteheart, buttonSize,2, getString(R.string.lbl_toggle_favorite), new View.OnClickListener() {
|
||||
TextUnderButton fav = new TextUnderButton(this, mBaseItem.getUserData().getIsFavorite() ? R.drawable.ic_heart_red : R.drawable.ic_heart, buttonSize,2, getString(R.string.lbl_favorite), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
UserItemDataDto data = mBaseItem.getUserData();
|
||||
@@ -620,7 +620,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
@Override
|
||||
public void onResponse(UserItemDataDto response) {
|
||||
mBaseItem.setUserData(response);
|
||||
((ImageButton) v).setImageResource(response.getIsFavorite() ? R.drawable.redheart : R.drawable.whiteheart);
|
||||
((ImageButton) v).setImageResource(response.getIsFavorite() ? R.drawable.ic_heart_red : R.drawable.ic_heart);
|
||||
TvApp.getApplication().setLastFavoriteUpdate(System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
@@ -631,9 +631,9 @@ public class ItemListActivity extends BaseActivity {
|
||||
|
||||
}
|
||||
|
||||
if ("Playlist".equals(mBaseItem.getType())) {
|
||||
if (mBaseItem.getBaseItemType() == BaseItemType.Playlist) {
|
||||
if (VIDEO_QUEUE.equals(mBaseItem.getId())) {
|
||||
mButtonRow.addView(new TextUnderButton(this, R.drawable.saveplaylist, buttonSize, 2, getString(R.string.lbl_save_as_playlist), new View.OnClickListener() {
|
||||
mButtonRow.addView(new TextUnderButton(this, R.drawable.ic_save, buttonSize, 2, getString(R.string.lbl_save_as_playlist), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MediaManager.saveVideoQueue(mActivity);
|
||||
@@ -641,7 +641,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
}));
|
||||
}
|
||||
|
||||
TextUnderButton delete = new TextUnderButton(this, R.drawable.trash, buttonSize, getString(R.string.lbl_delete), new View.OnClickListener() {
|
||||
TextUnderButton delete = new TextUnderButton(this, R.drawable.ic_trash, buttonSize, getString(R.string.lbl_delete), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
if (mBaseItem.getId().equals(VIDEO_QUEUE)) {
|
||||
@@ -702,7 +702,7 @@ public class ItemListActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
if (mBaseItem.getAlbumArtists() != null && mBaseItem.getAlbumArtists().size() > 0) {
|
||||
TextUnderButton artist = new TextUnderButton(this, R.drawable.user, buttonSize, 4, getString(R.string.lbl_open_artist), new View.OnClickListener() {
|
||||
TextUnderButton artist = new TextUnderButton(this, R.drawable.ic_user, buttonSize, 4, getString(R.string.lbl_open_artist), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent artist = new Intent(mActivity, FullDetailsActivity.class);
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import androidx.leanback.widget.Row;
|
||||
import android.view.View;
|
||||
|
||||
import org.jellyfin.androidtv.model.InfoItem;
|
||||
|
||||
@@ -3,15 +3,15 @@ package org.jellyfin.androidtv.details;
|
||||
import android.animation.Animator;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
@@ -56,7 +56,7 @@ public class PhotoPlayerActivity extends BaseActivity {
|
||||
boolean isPlaying;
|
||||
|
||||
FrameLayout mPopupArea;
|
||||
RowsFragment mPopupRowsFragment;
|
||||
RowsSupportFragment mPopupRowsFragment;
|
||||
ArrayObjectAdapter mPopupRowAdapter;
|
||||
ListRow mThumbRow;
|
||||
PositionableListRowPresenter mPopupRowPresenter;
|
||||
@@ -95,13 +95,13 @@ public class PhotoPlayerActivity extends BaseActivity {
|
||||
loadNext();
|
||||
loadPrev();
|
||||
|
||||
// Inject the RowsFragment in the popup container
|
||||
// Inject the RowsSupportFragment in the popup container
|
||||
if (getFragmentManager().findFragmentById(R.id.rows_area) == null) {
|
||||
mPopupRowsFragment = new RowsFragment();
|
||||
getFragmentManager().beginTransaction()
|
||||
mPopupRowsFragment = new RowsSupportFragment();
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.rows_area, mPopupRowsFragment).commit();
|
||||
} else {
|
||||
mPopupRowsFragment = (RowsFragment) getFragmentManager()
|
||||
mPopupRowsFragment = (RowsSupportFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.rows_area);
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ public class PhotoPlayerActivity extends BaseActivity {
|
||||
.resize(displayWidth, displayHeight)
|
||||
.centerInside()
|
||||
.skipMemoryCache()
|
||||
.error(R.drawable.photo)
|
||||
.error(R.drawable.tile_land_photo)
|
||||
.into(target, new Callback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
||||
@@ -154,7 +154,7 @@ public class TvApiEventListener extends ApiEventListener {
|
||||
switch (response.getItems()[0].getMediaType()) {
|
||||
case "Video":
|
||||
MediaManager.setCurrentVideoQueue(Arrays.asList(response.getItems()));
|
||||
Intent intent = new Intent(TvApp.getApplication().getCurrentActivity(), TvApp.getApplication().getPlaybackActivityClass(response.getItems()[0].getType()));
|
||||
Intent intent = new Intent(TvApp.getApplication().getCurrentActivity(), TvApp.getApplication().getPlaybackActivityClass(response.getItems()[0].getBaseItemType()));
|
||||
TvApp.getApplication().getCurrentActivity().startActivity(intent);
|
||||
break;
|
||||
case "Audio":
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.jellyfin.androidtv.imagehandling;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.jellyfin.androidtv.integration;
|
||||
|
||||
public class Recommendation {
|
||||
private RecommendationType type;
|
||||
private String itemId;
|
||||
private Integer recId;
|
||||
private long dateAdded;
|
||||
|
||||
public Recommendation(RecommendationType type, String id) {
|
||||
itemId = id;
|
||||
this.type = type;
|
||||
this.dateAdded = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public void setItemId(String mItemId) {
|
||||
this.itemId = mItemId;
|
||||
}
|
||||
|
||||
public Integer getRecId() {
|
||||
return recId;
|
||||
}
|
||||
|
||||
public void setRecId(Integer mRecId) {
|
||||
this.recId = mRecId;
|
||||
}
|
||||
|
||||
public long getDateAdded() {
|
||||
return dateAdded;
|
||||
}
|
||||
|
||||
public RecommendationType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(RecommendationType mType) {
|
||||
this.type = mType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof Recommendation) {
|
||||
Recommendation compare = (Recommendation)o;
|
||||
return compare.getType() == this.getType() && compare.getItemId().equals(this.getItemId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.jellyfin.androidtv.integration;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Created by Eric on 3/1/2015.
|
||||
*/
|
||||
public class RecommendationBuilder {
|
||||
private static final String TAG = "RecommendationBuilder";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private int mId;
|
||||
private int mPriority;
|
||||
private int mSmallIcon;
|
||||
private String mTitle;
|
||||
private String mDescription;
|
||||
private Bitmap mBitmap;
|
||||
private String mBackgroundUri;
|
||||
private PendingIntent mIntent;
|
||||
|
||||
public RecommendationBuilder() {
|
||||
}
|
||||
|
||||
public RecommendationBuilder setContext(Context context) {
|
||||
mContext = context;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setId(int id) {
|
||||
mId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setPriority(int priority) {
|
||||
mPriority = priority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setTitle(String title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setDescription(String description) {
|
||||
mDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setBitmap(Bitmap bitmap) {
|
||||
mBitmap = bitmap;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setBackground(String uri) {
|
||||
mBackgroundUri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setIntent(PendingIntent intent) {
|
||||
mIntent = intent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecommendationBuilder setSmallIcon(int resourceId) {
|
||||
mSmallIcon = resourceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Notification build() {
|
||||
|
||||
Log.d(TAG, "Building notification - " + this.toString());
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
if (mBackgroundUri != null) {
|
||||
try {
|
||||
Log.d(TAG, "Background - " + Uri.parse(RecommendationContentProvider.CONTENT_URI + URLEncoder.encode(mBackgroundUri, "UTF-8") + "/0").toString());
|
||||
extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, Uri.parse(RecommendationContentProvider.CONTENT_URI + URLEncoder.encode(mBackgroundUri, "UTF-8")).toString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Notification notification = new NotificationCompat.BigPictureStyle(
|
||||
new NotificationCompat.Builder(mContext)
|
||||
.setContentTitle(mTitle)
|
||||
.setContentText(mDescription)
|
||||
.setPriority(mPriority)
|
||||
.setLocalOnly(true)
|
||||
.setOngoing(true)
|
||||
.setColor(Utils.getBrandColor())
|
||||
.setCategory(Notification.CATEGORY_RECOMMENDATION)
|
||||
.setLargeIcon(mBitmap)
|
||||
.setSmallIcon(mSmallIcon)
|
||||
.setContentIntent(mIntent)
|
||||
.setExtras(extras))
|
||||
.build();
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecommendationBuilder{" +
|
||||
", mId=" + mId +
|
||||
", mPriority=" + mPriority +
|
||||
", mSmallIcon=" + mSmallIcon +
|
||||
", mTitle='" + mTitle + '\'' +
|
||||
", mDescription='" + mDescription + '\'' +
|
||||
", mBitmap='" + mBitmap + '\'' +
|
||||
", mBackgroundUri='" + mBackgroundUri + '\'' +
|
||||
", mIntent=" + mIntent +
|
||||
'}';
|
||||
}}
|
||||
@@ -1,157 +0,0 @@
|
||||
package org.jellyfin.androidtv.integration;
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2014 David Carver
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.OkUrlFactory;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
/**
|
||||
* Provides a background image for Recommendations for a RecommendationCardView.
|
||||
*
|
||||
* The card view changed to require a content provider be created. It requests
|
||||
* items via the openFile(Uri uri) method. This is an example of how to retrieve
|
||||
* an image from a web site and provide a ParcelFileDescriptor back that
|
||||
* contains the contents.
|
||||
*
|
||||
* This is based on code from the following stackoverflow description on how to
|
||||
* populate a ParcelFileDescriptor from any input stream.
|
||||
*
|
||||
* http://stackoverflow.com/a/14734310/1950264
|
||||
*
|
||||
* You still need to setup a ContentProvider entry and Authority in the
|
||||
* AndroidManifest.xml
|
||||
*
|
||||
* See
|
||||
* http://developer.android.com/reference/android/content/ContentProvider.html
|
||||
*
|
||||
*/
|
||||
public class RecommendationContentProvider extends ContentProvider {
|
||||
|
||||
public static String AUTHORITY = "org.jellyfin.androidtv.recommendations";
|
||||
public static String CONTENT_URI = "content://" + AUTHORITY + "/";
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode)
|
||||
throws FileNotFoundException {
|
||||
ParcelFileDescriptor[] pipe = null;
|
||||
|
||||
String url = uri.getPath();
|
||||
|
||||
try {
|
||||
String decodedUrl = URLDecoder.decode(url.replaceFirst("/", ""),
|
||||
"UTF-8");
|
||||
pipe = ParcelFileDescriptor.createPipe();
|
||||
|
||||
// This uses OKHttp to
|
||||
OkHttpClient httpClient = new OkHttpClient();
|
||||
HttpURLConnection connection = new OkUrlFactory(httpClient).open(new URL(decodedUrl));
|
||||
|
||||
new TransferThread(connection.getInputStream(),
|
||||
new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]))
|
||||
.start();
|
||||
} catch (IOException e) {
|
||||
Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
|
||||
throw new FileNotFoundException("Could not open pipe for: "
|
||||
+ uri.toString());
|
||||
}
|
||||
|
||||
return (pipe[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return "image/*";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static class TransferThread extends Thread {
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
|
||||
TransferThread(InputStream in, OutputStream out) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
|
||||
try {
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
|
||||
in.close();
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(getClass().getSimpleName(),
|
||||
"Exception transferring file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
package org.jellyfin.androidtv.integration;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.browsing.MainActivity;
|
||||
import org.jellyfin.androidtv.querying.StdItemQuery;
|
||||
import org.jellyfin.androidtv.startup.StartupActivity;
|
||||
import org.jellyfin.androidtv.util.ImageUtils;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.entities.LocationType;
|
||||
import org.jellyfin.apiclient.model.entities.SortOrder;
|
||||
import org.jellyfin.apiclient.model.querying.ItemFields;
|
||||
import org.jellyfin.apiclient.model.querying.ItemFilter;
|
||||
import org.jellyfin.apiclient.model.querying.ItemSortBy;
|
||||
import org.jellyfin.apiclient.model.querying.ItemsResult;
|
||||
import org.jellyfin.apiclient.model.querying.NextUpQuery;
|
||||
import org.jellyfin.apiclient.model.querying.SimilarItemsQuery;
|
||||
|
||||
/**
|
||||
* Created by Eric on 3/1/2015.
|
||||
*/
|
||||
public class RecommendationManager {
|
||||
private final String REC_FILE_NAME = "reccomendations.json";
|
||||
private final Integer MAX_TV_RECS = 3;
|
||||
private final Integer MAX_MOVIE_RECS = 4;
|
||||
|
||||
private boolean isEnabled;
|
||||
|
||||
private static RecommendationManager instance;
|
||||
|
||||
private Recommendations mRecommendations;
|
||||
|
||||
public RecommendationManager() {
|
||||
isEnabled = Build.VERSION.SDK_INT >= 21;
|
||||
mRecommendations = loadRecs();
|
||||
if (isEnabled) {
|
||||
validate();
|
||||
} else {
|
||||
TvApp.getApplication().getLogger().Info("Recommendations not enabled on this device");
|
||||
}
|
||||
}
|
||||
|
||||
public static RecommendationManager getInstance() {
|
||||
if (instance == null) instance = new RecommendationManager();
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
if (instance == null) {
|
||||
getInstance();
|
||||
} else {
|
||||
instance.validate();
|
||||
}
|
||||
}
|
||||
|
||||
private Recommendations loadRecs() {
|
||||
if (isEnabled) {
|
||||
try {
|
||||
InputStream recFile = TvApp.getApplication().openFileInput(REC_FILE_NAME);
|
||||
String json = Utils.readStringFromStream(recFile);
|
||||
recFile.close();
|
||||
return (Recommendations) TvApp.getApplication().getSerializer().DeserializeFromString(json, Recommendations.class);
|
||||
} catch (IOException e) {
|
||||
// none saved
|
||||
return new Recommendations(TvApp.getApplication().getApiClient().getServerInfo().getId(), TvApp.getApplication().getCurrentUser().getId());
|
||||
}
|
||||
} else {
|
||||
return new Recommendations(TvApp.getApplication().getApiClient().getServerInfo().getId(), TvApp.getApplication().getCurrentUser().getId());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean validate() {
|
||||
if (isEnabled) {
|
||||
//Now validate that these are for this server and user.
|
||||
if (mRecommendations == null || mRecommendations.getServerId() == null || TvApp.getApplication().getApiClient().getServerInfo() == null || !mRecommendations.getServerId().equals(TvApp.getApplication().getApiClient().getServerInfo().getId())
|
||||
|| mRecommendations.getUserId() == null || TvApp.getApplication().getCurrentUser() == null || !mRecommendations.getUserId().equals(TvApp.getApplication().getCurrentUser().getId())) {
|
||||
//Nope - clear them out and start over for this user
|
||||
clearAll();
|
||||
createAll();
|
||||
TvApp.getApplication().getLogger().Info("Recommendations re-set for user "+TvApp.getApplication().getCurrentUser().getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
createAll();
|
||||
TvApp.getApplication().getLogger().Info("Recommendations re-created for user "+TvApp.getApplication().getCurrentUser().getName());
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void saveRecs() {
|
||||
if (isEnabled) {
|
||||
try {
|
||||
OutputStream recFile = TvApp.getApplication().openFileOutput(REC_FILE_NAME, Context.MODE_PRIVATE);
|
||||
recFile.write(TvApp.getApplication().getSerializer().SerializeToString(mRecommendations).getBytes());
|
||||
recFile.close();
|
||||
} catch (IOException e) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Error saving recommendations",e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void clearAll() {
|
||||
NotificationManager nm = (NotificationManager)
|
||||
TvApp.getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
nm.cancelAll();
|
||||
mRecommendations = new Recommendations(TvApp.getApplication().getApiClient().getServerInfo().getId(), TvApp.getApplication().getCurrentUser().getId());
|
||||
saveRecs();
|
||||
}
|
||||
|
||||
public void createAll() {
|
||||
if (isEnabled) {
|
||||
//Create recs for next up tv and movies
|
||||
NextUpQuery nextUpQuery = new NextUpQuery();
|
||||
nextUpQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
nextUpQuery.setLimit(MAX_TV_RECS);
|
||||
nextUpQuery.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio});
|
||||
TvApp.getApplication().getApiClient().GetNextUpEpisodesAsync(nextUpQuery, new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult response) {
|
||||
if (response.getTotalRecordCount() > 0) {
|
||||
for (BaseItemDto episode : response.getItems()) {
|
||||
if (episode.getLocationType() != LocationType.Virtual) addRecommendation(episode, RecommendationType.Tv);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//First try for resumables
|
||||
StdItemQuery resumeMovies = new StdItemQuery();
|
||||
resumeMovies.setIncludeItemTypes(new String[]{"Movie"});
|
||||
resumeMovies.setRecursive(true);
|
||||
resumeMovies.setLimit(2);
|
||||
resumeMovies.setFilters(new ItemFilter[]{ItemFilter.IsResumable});
|
||||
resumeMovies.setSortBy(new String[]{ItemSortBy.DatePlayed});
|
||||
resumeMovies.setSortOrder(SortOrder.Descending);
|
||||
TvApp.getApplication().getApiClient().GetItemsAsync(resumeMovies, new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult response) {
|
||||
int movieItems = 0;
|
||||
if (response.getTotalRecordCount() > 0) {
|
||||
for (BaseItemDto movie : response.getItems()) {
|
||||
recommend(movie.getId());
|
||||
movieItems++;
|
||||
}
|
||||
}
|
||||
if (movieItems < MAX_MOVIE_RECS) {
|
||||
//Now fill in with latest movies
|
||||
StdItemQuery suggMovies = new StdItemQuery();
|
||||
suggMovies.setIncludeItemTypes(new String[]{"Movie"});
|
||||
suggMovies.setRecursive(true);
|
||||
suggMovies.setLimit(MAX_MOVIE_RECS - movieItems);
|
||||
suggMovies.setFilters(new ItemFilter[]{ItemFilter.IsUnplayed});
|
||||
suggMovies.setSortBy(new String[]{ItemSortBy.DateCreated});
|
||||
suggMovies.setSortOrder(SortOrder.Descending);
|
||||
TvApp.getApplication().getApiClient().GetItemsAsync(suggMovies, new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult suggResponse) {
|
||||
if (suggResponse.getTotalRecordCount() > 0) {
|
||||
for (BaseItemDto movie : suggResponse.getItems()) {
|
||||
addRecommendation(movie, RecommendationType.Movie);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void recommend(final String itemId) {
|
||||
if (isEnabled) {
|
||||
if (itemId == null) {
|
||||
TvApp.getApplication().getLogger().Error("Attempt to recommend null Item");
|
||||
return;
|
||||
}
|
||||
//No matter what it is, if it is resumable, recommend this item (need to re-retrieve for current user data)
|
||||
TvApp.getApplication().getApiClient().GetItemAsync(itemId, TvApp.getApplication().getCurrentUser().getId(), new Response<BaseItemDto>() {
|
||||
@Override
|
||||
public void onResponse(BaseItemDto response) {
|
||||
if (response == null) {
|
||||
TvApp.getApplication().getLogger().Error("No item found with ID: "+itemId);
|
||||
return;
|
||||
}
|
||||
if (response.getCanResume()) {
|
||||
addRecommendation(response, RecommendationType.Movie);
|
||||
} else {
|
||||
switch (response.getType()) {
|
||||
case "Movie":
|
||||
//First remove us if we were a recommendation
|
||||
mRecommendations.remove(RecommendationType.Movie, response.getId());
|
||||
|
||||
//Suggest a similar movie
|
||||
SimilarItemsQuery similar = new SimilarItemsQuery();
|
||||
similar.setId(response.getId());
|
||||
similar.setLimit(1);
|
||||
similar.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
TvApp.getApplication().getApiClient().GetSimilarItems(similar, new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult similarResponse) {
|
||||
if (similarResponse.getTotalRecordCount() > 0) {
|
||||
addRecommendation(similarResponse.getItems()[0], RecommendationType.Movie);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Error retrieving item for recommendation", exception);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "Episode":
|
||||
//First remove us if we were a recommendation
|
||||
mRecommendations.remove(RecommendationType.Tv, response.getId());
|
||||
|
||||
//Suggest the next up episode
|
||||
NextUpQuery next = new NextUpQuery();
|
||||
next.setSeriesId(response.getSeriesId());
|
||||
next.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
next.setLimit(1);
|
||||
TvApp.getApplication().getApiClient().GetNextUpEpisodesAsync(next, new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult nextResponse) {
|
||||
if (nextResponse.getTotalRecordCount() > 0 && nextResponse.getItems()[0].getLocationType() != LocationType.Virtual) {
|
||||
addRecommendation(nextResponse.getItems()[0], RecommendationType.Tv);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Error retrieving item for recommendation", exception);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Error retrieving item for recommendation", exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean addRecommendation(BaseItemDto item, RecommendationType type) {
|
||||
if (isEnabled) {
|
||||
//No need if we are already there
|
||||
if (mRecommendations.get(type, item.getId()) != null) return false;
|
||||
|
||||
//Not so build one
|
||||
new AsyncRunner().execute(item, type);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class AsyncRunner extends AsyncTask<Object, Integer, Boolean> {
|
||||
@Override
|
||||
protected Boolean doInBackground(Object... params) {
|
||||
BaseItemDto item = (BaseItemDto) params[0];
|
||||
RecommendationType type = (RecommendationType) params[1];
|
||||
Recommendation rec = new Recommendation(type, item.getId());
|
||||
rec.setRecId(mRecommendations.getRecId(type, type == RecommendationType.Movie ? MAX_MOVIE_RECS : MAX_TV_RECS));
|
||||
RecommendationBuilder builder = new RecommendationBuilder()
|
||||
.setContext(TvApp.getApplication())
|
||||
.setSmallIcon(R.drawable.logoicon114);
|
||||
|
||||
Notification recommendation = builder
|
||||
.setId(rec.getRecId())
|
||||
.setPriority(0)
|
||||
.setTitle(item.getName())
|
||||
.setDescription(item.getOverview())
|
||||
.setBitmap(Utils.getBitmapFromURL(ImageUtils.getPrimaryImageUrl(item, TvApp.getApplication().getApiClient(), true, true, false, 300)))
|
||||
.setBackground(ImageUtils.getBackdropImageUrl(item, TvApp.getApplication().getApiClient(), true))
|
||||
.setIntent(buildPendingIntent(item))
|
||||
.build();
|
||||
|
||||
mRecommendations.add(rec);
|
||||
saveRecs();
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager)
|
||||
TvApp.getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(rec.getRecId(), recommendation);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent buildPendingIntent(BaseItemDto item) {
|
||||
Intent directIntent = new Intent(TvApp.getApplication(), StartupActivity.class);
|
||||
directIntent.putExtra("ItemId", item.getId());
|
||||
directIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(TvApp.getApplication());
|
||||
stackBuilder.addParentStack(MainActivity.class);
|
||||
stackBuilder.addNextIntent(directIntent);
|
||||
// Ensure a unique PendingIntents, otherwise all recommendations end up with the same
|
||||
// PendingIntent
|
||||
directIntent.setAction(item.getId());
|
||||
|
||||
return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package org.jellyfin.androidtv.integration;
|
||||
|
||||
public enum RecommendationType {
|
||||
Movie,
|
||||
Tv
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package org.jellyfin.androidtv.integration;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Eric on 3/1/2015.
|
||||
*/
|
||||
public class Recommendations {
|
||||
|
||||
private String serverId;
|
||||
private String userId;
|
||||
private List<Recommendation> tvRecommendations = new ArrayList<>();
|
||||
private List<Recommendation> movieRecommendations = new ArrayList<>();
|
||||
|
||||
public Recommendations(String serverId, String userId) {
|
||||
this.userId = userId;
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public List<Recommendation> getTvRecommendations() {
|
||||
return tvRecommendations;
|
||||
}
|
||||
|
||||
public void setTvRecommendations(List<Recommendation> tvRecommendations) {
|
||||
this.tvRecommendations = tvRecommendations;
|
||||
}
|
||||
|
||||
public List<Recommendation> getMovieRecommendations() {
|
||||
return movieRecommendations;
|
||||
}
|
||||
|
||||
public void setMovieRecommendations(List<Recommendation> movieRecommendations) {
|
||||
this.movieRecommendations = movieRecommendations;
|
||||
}
|
||||
|
||||
public Recommendation get(RecommendationType type, String id) {
|
||||
Recommendation compare = new Recommendation(type, id);
|
||||
switch (type) {
|
||||
|
||||
case Movie:
|
||||
return movieRecommendations.contains(compare) ? movieRecommendations.get(movieRecommendations.indexOf(compare)) : null;
|
||||
case Tv:
|
||||
return tvRecommendations.contains(compare) ? tvRecommendations.get(tvRecommendations.indexOf(compare)) : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean add(Recommendation rec) {
|
||||
switch (rec.getType()) {
|
||||
|
||||
case Movie:
|
||||
movieRecommendations.add(rec);
|
||||
break;
|
||||
case Tv:
|
||||
tvRecommendations.add(rec);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Integer getRecId(RecommendationType type, int max) {
|
||||
switch (type) {
|
||||
case Movie:
|
||||
if (movieRecommendations.size() < max) return 100 + movieRecommendations.size()+1;
|
||||
return replaceOldest(type);
|
||||
case Tv:
|
||||
if (tvRecommendations.size() < max) return 200 + tvRecommendations.size()+1;
|
||||
return replaceOldest(type);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("type");
|
||||
}
|
||||
|
||||
private Integer replaceOldest(RecommendationType type) {
|
||||
List<Recommendation> list = type == RecommendationType.Movie ? movieRecommendations : tvRecommendations;
|
||||
|
||||
Recommendation oldest = Collections.min(list, new Comparator<Recommendation>() {
|
||||
@Override
|
||||
public int compare(Recommendation lhs, Recommendation rhs) {
|
||||
return Long.compare(lhs.getDateAdded(), rhs.getDateAdded());
|
||||
}
|
||||
});
|
||||
|
||||
list.remove(oldest);
|
||||
((NotificationManager) TvApp.getApplication().getSystemService(Context.NOTIFICATION_SERVICE)).cancel(oldest.getRecId());
|
||||
return oldest.getRecId();
|
||||
}
|
||||
|
||||
public boolean remove(RecommendationType type, String id) {
|
||||
List<Recommendation> list = type == RecommendationType.Movie ? movieRecommendations : tvRecommendations;
|
||||
|
||||
Recommendation existing = get(type, id);
|
||||
|
||||
if (existing != null) {
|
||||
list.remove(existing);
|
||||
((NotificationManager) TvApp.getApplication().getSystemService(Context.NOTIFICATION_SERVICE)).cancel(existing.getRecId());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.BaseItemUtils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
@@ -20,6 +21,7 @@ import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.apiclient.ServerInfo;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemPerson;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
import org.jellyfin.apiclient.model.entities.ImageType;
|
||||
import org.jellyfin.apiclient.model.livetv.ChannelInfoDto;
|
||||
@@ -43,7 +45,6 @@ public class BaseRowItem {
|
||||
private SelectAction selectAction = SelectAction.ShowDetails;
|
||||
private boolean isPlaying;
|
||||
|
||||
|
||||
public BaseRowItem(int index, BaseItemDto item) {
|
||||
this(index, item, false, false);
|
||||
}
|
||||
@@ -51,10 +52,11 @@ public class BaseRowItem {
|
||||
public BaseRowItem(int index, BaseItemDto item, boolean preferParentThumb, boolean staticHeight) {
|
||||
this(index, item, preferParentThumb, staticHeight, SelectAction.ShowDetails);
|
||||
}
|
||||
|
||||
public BaseRowItem(int index, BaseItemDto item, boolean preferParentThumb, boolean staticHeight, SelectAction selectAction) {
|
||||
this.index = index;
|
||||
this.baseItem = item;
|
||||
type = item.getType().equals("Program") ? ItemType.LiveTvProgram : item.getType().equals("Recording") ? ItemType.LiveTvRecording : ItemType.BaseItem;
|
||||
this.type = item.getBaseItemType() == BaseItemType.Program ? ItemType.LiveTvProgram : item.getBaseItemType() == BaseItemType.Recording ? ItemType.LiveTvRecording : ItemType.BaseItem;
|
||||
this.preferParentThumb = preferParentThumb;
|
||||
this.staticHeight = staticHeight;
|
||||
this.selectAction = selectAction;
|
||||
@@ -63,10 +65,12 @@ public class BaseRowItem {
|
||||
public BaseRowItem(int index, ChannelInfoDto channel) {
|
||||
this.index = index;
|
||||
this.channelInfo = channel;
|
||||
type = ItemType.LiveTvChannel;
|
||||
this.type = ItemType.LiveTvChannel;
|
||||
}
|
||||
|
||||
public BaseRowItem(BaseItemDto program, boolean staticHeight) { this(0, program, false, staticHeight); }
|
||||
public BaseRowItem(BaseItemDto program, boolean staticHeight) {
|
||||
this(0, program, false, staticHeight);
|
||||
}
|
||||
|
||||
public BaseRowItem(BaseItemDto program) {
|
||||
this(0, program);
|
||||
@@ -85,12 +89,12 @@ public class BaseRowItem {
|
||||
public BaseRowItem(BaseItemPerson person) {
|
||||
this.person = person;
|
||||
this.staticHeight = true;
|
||||
type = ItemType.Person;
|
||||
this.type = ItemType.Person;
|
||||
}
|
||||
|
||||
public BaseRowItem(UserDto user) {
|
||||
this.user = user;
|
||||
type = ItemType.User;
|
||||
this.type = ItemType.User;
|
||||
}
|
||||
|
||||
public BaseRowItem(SearchHint hint) {
|
||||
@@ -101,47 +105,97 @@ public class BaseRowItem {
|
||||
public BaseRowItem(ChapterItemInfo chapter) {
|
||||
this.chapterInfo = chapter;
|
||||
this.staticHeight = true;
|
||||
type = ItemType.Chapter;
|
||||
this.type = ItemType.Chapter;
|
||||
}
|
||||
|
||||
public BaseRowItem(GridButton button) {
|
||||
this.gridButton = button;
|
||||
type = ItemType.GridButton;
|
||||
staticHeight = true;
|
||||
this.type = ItemType.GridButton;
|
||||
this.staticHeight = true;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
public void setIndex(int ndx) { index = ndx; }
|
||||
|
||||
public void setIndex(int ndx) {
|
||||
index = ndx;
|
||||
}
|
||||
|
||||
public BaseItemDto getBaseItem() {
|
||||
return baseItem;
|
||||
}
|
||||
public BaseItemPerson getPerson() { return person; }
|
||||
public ChapterItemInfo getChapterInfo() { return chapterInfo; }
|
||||
public ServerInfo getServerInfo() { return serverInfo; }
|
||||
public UserDto getUser() { return user; }
|
||||
public SearchHint getSearchHint() { return searchHint; }
|
||||
public ChannelInfoDto getChannelInfo() { return channelInfo; }
|
||||
public BaseItemDto getProgramInfo() { return baseItem; }
|
||||
public BaseItemDto getRecordingInfo() { return baseItem; }
|
||||
public SeriesTimerInfoDto getSeriesTimerInfo() { return seriesTimerInfo; }
|
||||
public GridButton getGridButton() { return gridButton; }
|
||||
|
||||
public boolean isChapter() { return type == ItemType.Chapter; }
|
||||
public boolean isPerson() { return type == ItemType.Person; }
|
||||
public boolean isBaseItem() { return type == ItemType.BaseItem; }
|
||||
public boolean getPreferParentThumb() { return preferParentThumb; }
|
||||
public ItemType getItemType() { return type; }
|
||||
public BaseItemPerson getPerson() {
|
||||
return person;
|
||||
}
|
||||
|
||||
public ChapterItemInfo getChapterInfo() {
|
||||
return chapterInfo;
|
||||
}
|
||||
|
||||
public ServerInfo getServerInfo() {
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
public UserDto getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public SearchHint getSearchHint() {
|
||||
return searchHint;
|
||||
}
|
||||
|
||||
public ChannelInfoDto getChannelInfo() {
|
||||
return channelInfo;
|
||||
}
|
||||
|
||||
public BaseItemDto getProgramInfo() {
|
||||
return baseItem;
|
||||
}
|
||||
|
||||
public BaseItemDto getRecordingInfo() {
|
||||
return baseItem;
|
||||
}
|
||||
|
||||
public SeriesTimerInfoDto getSeriesTimerInfo() {
|
||||
return seriesTimerInfo;
|
||||
}
|
||||
|
||||
public GridButton getGridButton() {
|
||||
return gridButton;
|
||||
}
|
||||
|
||||
public boolean isChapter() {
|
||||
return type == ItemType.Chapter;
|
||||
}
|
||||
|
||||
public boolean isPerson() {
|
||||
return type == ItemType.Person;
|
||||
}
|
||||
|
||||
public boolean isBaseItem() {
|
||||
return type == ItemType.BaseItem;
|
||||
}
|
||||
|
||||
public boolean getPreferParentThumb() {
|
||||
return preferParentThumb;
|
||||
}
|
||||
|
||||
public ItemType getItemType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isFolder() {
|
||||
return type == ItemType.BaseItem && baseItem != null && baseItem.getIsFolderItem();
|
||||
}
|
||||
public boolean showCardInfoOverlay() {return type == ItemType.BaseItem && baseItem != null
|
||||
&& ("Folder".equals(baseItem.getType()) || "PhotoAlbum".equals(baseItem.getType()) || "RecordingGroup".equals(baseItem.getType())
|
||||
|| "UserView".equals(baseItem.getType()) || "CollectionFolder".equals(baseItem.getType()) || "Photo".equals(baseItem.getType())
|
||||
|| "Video".equals(baseItem.getType()) || "Person".equals(baseItem.getType()) || "Playlist".equals(baseItem.getType())
|
||||
|| "MusicArtist".equals(baseItem.getType()));
|
||||
|
||||
public boolean showCardInfoOverlay() {
|
||||
return type == ItemType.BaseItem && baseItem != null
|
||||
&& Arrays.asList(BaseItemType.Folder, BaseItemType.PhotoAlbum, BaseItemType.RecordingGroup,
|
||||
BaseItemType.UserView, BaseItemType.CollectionFolder, BaseItemType.Photo,
|
||||
BaseItemType.Video, BaseItemType.Person, BaseItemType.Playlist,
|
||||
BaseItemType.MusicArtist).contains(baseItem.getBaseItemType());
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
@@ -155,7 +209,8 @@ public class BaseRowItem {
|
||||
case SeriesTimer:
|
||||
return seriesTimerInfo != null;
|
||||
default:
|
||||
return true; //compatibility
|
||||
// compatibility
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,14 +227,13 @@ public class BaseRowItem {
|
||||
default:
|
||||
return getPrimaryImageUrl(maxHeight);
|
||||
}
|
||||
default:
|
||||
return getPrimaryImageUrl(maxHeight);
|
||||
default:
|
||||
return getPrimaryImageUrl(maxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrimaryImageUrl(int maxHeight) {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvProgram:
|
||||
case LiveTvRecording:
|
||||
@@ -193,37 +247,33 @@ public class BaseRowItem {
|
||||
case LiveTvChannel:
|
||||
return ImageUtils.getPrimaryImageUrl(channelInfo, TvApp.getApplication().getApiClient());
|
||||
case Server:
|
||||
return "android.resource://org.jellyfin.androidtv/" + R.drawable.server;
|
||||
return ImageUtils.getResourceUrl(R.drawable.tile_port_server);
|
||||
case GridButton:
|
||||
return "android.resource://org.jellyfin.androidtv/" + gridButton.getImageIndex();
|
||||
return ImageUtils.getResourceUrl(gridButton.getImageIndex());
|
||||
case SeriesTimer:
|
||||
return "android.resource://org.jellyfin.androidtv/" + R.drawable.seriestimer;
|
||||
return ImageUtils.getResourceUrl(R.drawable.tile_land_series_timer);
|
||||
case SearchHint:
|
||||
return Utils.isNonEmpty(searchHint.getPrimaryImageTag()) ? ImageUtils.getImageUrl(searchHint.getItemId(), ImageType.Primary, searchHint.getPrimaryImageTag(), TvApp.getApplication().getApiClient()) :
|
||||
Utils.isNonEmpty(searchHint.getThumbImageItemId()) ? ImageUtils.getImageUrl(searchHint.getThumbImageItemId(), ImageType.Thumb, searchHint.getThumbImageTag(), TvApp.getApplication().getApiClient()) : null;
|
||||
if (Utils.isNonEmpty(searchHint.getPrimaryImageTag())) {
|
||||
return ImageUtils.getImageUrl(searchHint.getItemId(), ImageType.Primary, searchHint.getPrimaryImageTag(), TvApp.getApplication().getApiClient());
|
||||
} else if (Utils.isNonEmpty(searchHint.getThumbImageItemId())) {
|
||||
return ImageUtils.getImageUrl(searchHint.getThumbImageItemId(), ImageType.Thumb, searchHint.getThumbImageTag(), TvApp.getApplication().getApiClient());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isFavorite() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvRecording:
|
||||
case LiveTvProgram:
|
||||
return baseItem.getUserData() != null && baseItem.getUserData().getIsFavorite();
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
break;
|
||||
case LiveTvChannel:
|
||||
break;
|
||||
case GridButton:
|
||||
break;
|
||||
}
|
||||
@@ -238,20 +288,13 @@ public class BaseRowItem {
|
||||
case LiveTvProgram:
|
||||
return baseItem.getUserData() != null && baseItem.getUserData().getPlayed();
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
break;
|
||||
case LiveTvChannel:
|
||||
break;
|
||||
case GridButton:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -260,7 +303,9 @@ public class BaseRowItem {
|
||||
public String getCardName() {
|
||||
switch (type) {
|
||||
case BaseItem:
|
||||
if ("Audio".equals(baseItem.getType())) return baseItem.getAlbumArtist() != null ? baseItem.getAlbumArtist() : baseItem.getAlbum() != null ? baseItem.getAlbum() : "<Unknown>";
|
||||
if (baseItem.getBaseItemType() == BaseItemType.Audio) {
|
||||
return baseItem.getAlbumArtist() != null ? baseItem.getAlbumArtist() : baseItem.getAlbum() != null ? baseItem.getAlbum() : "<Unknown>";
|
||||
}
|
||||
default:
|
||||
return getFullName();
|
||||
}
|
||||
@@ -268,7 +313,6 @@ public class BaseRowItem {
|
||||
|
||||
public String getFullName() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvProgram:
|
||||
case LiveTvRecording:
|
||||
@@ -296,11 +340,10 @@ public class BaseRowItem {
|
||||
|
||||
public String getName() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvRecording:
|
||||
case LiveTvProgram:
|
||||
return "Audio".equals(baseItem.getType())? getFullName() : baseItem.getName();
|
||||
return baseItem.getBaseItemType() == BaseItemType.Audio ? getFullName() : baseItem.getName();
|
||||
case Person:
|
||||
return person.getName();
|
||||
case Server:
|
||||
@@ -324,7 +367,6 @@ public class BaseRowItem {
|
||||
|
||||
public String getItemId() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvProgram:
|
||||
case LiveTvRecording:
|
||||
@@ -352,7 +394,6 @@ public class BaseRowItem {
|
||||
|
||||
public String getSubText() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
return BaseItemUtils.getSubName(baseItem);
|
||||
case Person:
|
||||
@@ -361,7 +402,7 @@ public class BaseRowItem {
|
||||
Long pos = chapterInfo.getStartPositionTicks() / 10000;
|
||||
return TimeUtils.formatMillis(pos.intValue());
|
||||
case Server:
|
||||
return serverInfo.getLocalAddress() != null ? serverInfo.getLocalAddress().substring(7) : "";
|
||||
return serverInfo.getAddress() != null ? serverInfo.getAddress().substring(7) : "";
|
||||
case LiveTvChannel:
|
||||
return channelInfo.getNumber();
|
||||
case LiveTvProgram:
|
||||
@@ -383,54 +424,25 @@ public class BaseRowItem {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvRecording:
|
||||
case LiveTvProgram:
|
||||
return baseItem.getType();
|
||||
case Person:
|
||||
return person.getType();
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
return searchHint.getType();
|
||||
case LiveTvChannel:
|
||||
return channelInfo.getType();
|
||||
case GridButton:
|
||||
return "GridButton";
|
||||
case SeriesTimer:
|
||||
return "SeriesTimer";
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
public BaseItemType getBaseItemType() {
|
||||
if (baseItem != null)
|
||||
return baseItem.getBaseItemType();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
case LiveTvRecording:
|
||||
case LiveTvProgram:
|
||||
return baseItem.getOverview();
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
break;
|
||||
case LiveTvChannel:
|
||||
break;
|
||||
case GridButton:
|
||||
break;
|
||||
case SeriesTimer:
|
||||
@@ -442,22 +454,15 @@ public class BaseRowItem {
|
||||
|
||||
public long getRuntimeTicks() {
|
||||
switch (type) {
|
||||
|
||||
case LiveTvRecording:
|
||||
case BaseItem:
|
||||
return baseItem.getRunTimeTicks() != null ? baseItem.getRunTimeTicks() : 0;
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
break;
|
||||
case LiveTvChannel:
|
||||
break;
|
||||
case GridButton:
|
||||
break;
|
||||
case LiveTvProgram:
|
||||
@@ -469,25 +474,16 @@ public class BaseRowItem {
|
||||
|
||||
public int getChildCount() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
return isFolder() && !"MusicArtist".equals(baseItem.getType()) && baseItem.getChildCount() != null ? baseItem.getChildCount() : -1;
|
||||
return isFolder() && baseItem.getBaseItemType() != BaseItemType.MusicArtist && baseItem.getChildCount() != null ? baseItem.getChildCount() : -1;
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
break;
|
||||
case LiveTvChannel:
|
||||
break;
|
||||
case LiveTvRecording:
|
||||
break;
|
||||
case GridButton:
|
||||
break;
|
||||
case LiveTvProgram:
|
||||
break;
|
||||
}
|
||||
@@ -496,12 +492,11 @@ public class BaseRowItem {
|
||||
}
|
||||
|
||||
public String getChildCountStr() {
|
||||
if (baseItem != null && "Playlist".equals(baseItem.getType()) && baseItem.getCumulativeRunTimeTicks() != null) {
|
||||
if (baseItem != null && baseItem.getBaseItemType() == BaseItemType.Playlist && baseItem.getCumulativeRunTimeTicks() != null) {
|
||||
return TimeUtils.formatMillis(baseItem.getCumulativeRunTimeTicks() / 10000);
|
||||
} else {
|
||||
Integer count = getChildCount();
|
||||
return count > 0 ? count.toString() : "";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +504,6 @@ public class BaseRowItem {
|
||||
switch (type) {
|
||||
case BaseItem:
|
||||
return ImageUtils.getBackdropImageUrl(baseItem, TvApp.getApplication().getConnectionManager().GetApiClient(baseItem), true);
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -517,26 +511,24 @@ public class BaseRowItem {
|
||||
|
||||
public Drawable getBadgeImage() {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
if (baseItem.getType().equals("Movie") && baseItem.getCriticRating() != null) {
|
||||
if (baseItem.getBaseItemType() == BaseItemType.Movie && baseItem.getCriticRating() != null) {
|
||||
return baseItem.getCriticRating() > 59 ? TvApp.getApplication().getDrawableCompat(R.drawable.fresh) : TvApp.getApplication().getDrawableCompat(R.drawable.rotten);
|
||||
} else if (baseItem.getType().equals("Program") && baseItem.getTimerId() != null) {
|
||||
return baseItem.getSeriesTimerId() != null ? TvApp.getApplication().getDrawableCompat(R.drawable.recseries) : TvApp.getApplication().getDrawableCompat(R.drawable.rec);
|
||||
} else if (baseItem.getBaseItemType() == BaseItemType.Program && baseItem.getTimerId() != null) {
|
||||
return baseItem.getSeriesTimerId() != null ? TvApp.getApplication().getDrawableCompat(R.drawable.ic_record_series_red) : TvApp.getApplication().getDrawableCompat(R.drawable.ic_record_red);
|
||||
}
|
||||
break;
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
if (user.getHasPassword()) {
|
||||
return TvApp.getApplication().getDrawableCompat(R.drawable.lock);
|
||||
return TvApp.getApplication().getDrawableCompat(R.drawable.ic_lock);
|
||||
}
|
||||
break;
|
||||
case LiveTvProgram:
|
||||
if (baseItem.getTimerId() != null) {
|
||||
return baseItem.getSeriesTimerId() != null ? TvApp.getApplication().getDrawableCompat(R.drawable.recseries) : TvApp.getApplication().getDrawableCompat(R.drawable.rec);
|
||||
return baseItem.getSeriesTimerId() != null ? TvApp.getApplication().getDrawableCompat(R.drawable.ic_record_series_red) : TvApp.getApplication().getDrawableCompat(R.drawable.ic_record_red);
|
||||
}
|
||||
case Chapter:
|
||||
break;
|
||||
@@ -547,7 +539,6 @@ public class BaseRowItem {
|
||||
|
||||
public void refresh(final EmptyResponse outerResponse) {
|
||||
switch (type) {
|
||||
|
||||
case BaseItem:
|
||||
TvApp.getApplication().getApiClient().GetItemAsync(getItemId(), TvApp.getApplication().getCurrentUser().getId(), new Response<BaseItemDto>() {
|
||||
@Override
|
||||
@@ -558,21 +549,13 @@ public class BaseRowItem {
|
||||
});
|
||||
break;
|
||||
case Person:
|
||||
break;
|
||||
case Server:
|
||||
break;
|
||||
case User:
|
||||
break;
|
||||
case Chapter:
|
||||
break;
|
||||
case SearchHint:
|
||||
break;
|
||||
case LiveTvChannel:
|
||||
break;
|
||||
case LiveTvRecording:
|
||||
break;
|
||||
case GridButton:
|
||||
break;
|
||||
case LiveTvProgram:
|
||||
break;
|
||||
}
|
||||
@@ -585,13 +568,28 @@ public class BaseRowItem {
|
||||
public boolean isStaticHeight() {
|
||||
return staticHeight;
|
||||
}
|
||||
public boolean isPlaying() { return isPlaying; }
|
||||
public void setIsPlaying(boolean value) { isPlaying = value; }
|
||||
|
||||
public boolean isPlaying() {
|
||||
return isPlaying;
|
||||
}
|
||||
|
||||
public void setIsPlaying(boolean value) {
|
||||
isPlaying = value;
|
||||
}
|
||||
|
||||
public enum ItemType {
|
||||
BaseItem,
|
||||
Person,
|
||||
Server, User, Chapter, SearchHint, LiveTvChannel, LiveTvRecording, GridButton, SeriesTimer, LiveTvProgram
|
||||
Server,
|
||||
User,
|
||||
Chapter,
|
||||
SearchHint,
|
||||
LiveTvChannel,
|
||||
LiveTvRecording,
|
||||
GridButton,
|
||||
SeriesTimer,
|
||||
LiveTvProgram,
|
||||
|
||||
}
|
||||
|
||||
public enum SelectAction {
|
||||
@@ -599,4 +597,3 @@ public class BaseRowItem {
|
||||
Play
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.jellyfin.androidtv.browsing.BrowseScheduleActivity;
|
||||
import org.jellyfin.androidtv.browsing.CollectionActivity;
|
||||
import org.jellyfin.androidtv.browsing.GenericFolderActivity;
|
||||
import org.jellyfin.androidtv.browsing.GenericGridActivity;
|
||||
import org.jellyfin.androidtv.browsing.MainActivity;
|
||||
import org.jellyfin.androidtv.browsing.UserViewActivity;
|
||||
import org.jellyfin.androidtv.details.FullDetailsActivity;
|
||||
import org.jellyfin.androidtv.details.ItemListActivity;
|
||||
@@ -21,8 +20,6 @@ import org.jellyfin.androidtv.livetv.LiveTvGuideActivity;
|
||||
import org.jellyfin.androidtv.model.ChapterItemInfo;
|
||||
import org.jellyfin.androidtv.model.ViewType;
|
||||
import org.jellyfin.androidtv.playback.MediaManager;
|
||||
import org.jellyfin.androidtv.startup.SelectUserActivity;
|
||||
import org.jellyfin.androidtv.util.DelayedMessage;
|
||||
import org.jellyfin.androidtv.util.KeyProcessor;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
@@ -31,11 +28,9 @@ import org.jellyfin.androidtv.util.apiclient.PlaybackHelper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.ConnectionResult;
|
||||
import org.jellyfin.apiclient.interaction.IConnectionManager;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.apiclient.ServerInfo;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
import org.jellyfin.apiclient.model.entities.DisplayPreferences;
|
||||
import org.jellyfin.apiclient.model.library.PlayAccess;
|
||||
@@ -56,27 +51,28 @@ public class ItemLauncher {
|
||||
case BaseItem:
|
||||
final BaseItemDto baseItem = rowItem.getBaseItem();
|
||||
try {
|
||||
TvApp.getApplication().getLogger().Debug("Item selected: " + rowItem.getIndex() + " - " + baseItem.getName() + " (" + baseItem.getType() + ")");
|
||||
TvApp.getApplication().getLogger().Debug("Item selected: " + rowItem.getIndex() + " - " + baseItem.getName() + " (" + baseItem.getBaseItemType() + ")");
|
||||
} catch (Exception e) {
|
||||
//swallow it
|
||||
}
|
||||
|
||||
//specialized type handling
|
||||
switch (baseItem.getType()) {
|
||||
case "UserView":
|
||||
case "CollectionFolder":
|
||||
switch (baseItem.getBaseItemType()) {
|
||||
case UserView:
|
||||
case CollectionFolder:
|
||||
//We need to get display prefs...
|
||||
TvApp.getApplication().getDisplayPrefsAsync(baseItem.getDisplayPreferencesId(), new Response<DisplayPreferences>() {
|
||||
@Override
|
||||
public void onResponse(DisplayPreferences response) {
|
||||
if (baseItem.getCollectionType() == null)
|
||||
if (baseItem.getCollectionType() == null) {
|
||||
baseItem.setCollectionType("unknown");
|
||||
TvApp.getApplication().getLogger().Debug("**** Collection type: "+baseItem.getCollectionType());
|
||||
}
|
||||
TvApp.getApplication().getLogger().Debug("**** Collection type: " + baseItem.getCollectionType());
|
||||
switch (baseItem.getCollectionType()) {
|
||||
case "movies":
|
||||
case "tvshows":
|
||||
case "music":
|
||||
TvApp.getApplication().getLogger().Debug("**** View Type Pref: "+response.getCustomPrefs().get("DefaultView"));
|
||||
TvApp.getApplication().getLogger().Debug("**** View Type Pref: " + response.getCustomPrefs().get("DefaultView"));
|
||||
if (ViewType.GRID.equals(response.getCustomPrefs().get("DefaultView"))) {
|
||||
// open grid browsing
|
||||
Intent folderIntent = new Intent(activity, GenericGridActivity.class);
|
||||
@@ -109,54 +105,62 @@ public class ItemLauncher {
|
||||
}
|
||||
});
|
||||
return;
|
||||
case "Series":
|
||||
case "MusicArtist":
|
||||
case Series:
|
||||
case MusicArtist:
|
||||
//Start activity for details display
|
||||
Intent intent = new Intent(activity, FullDetailsActivity.class);
|
||||
intent.putExtra("ItemId", baseItem.getId());
|
||||
if (noHistory) intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
|
||||
activity.startActivity(intent);
|
||||
|
||||
return;
|
||||
|
||||
case "MusicAlbum":
|
||||
case "Playlist":
|
||||
case MusicAlbum:
|
||||
case Playlist:
|
||||
//Start activity for song list display
|
||||
Intent songListIntent = new Intent(activity, ItemListActivity.class);
|
||||
songListIntent.putExtra("ItemId", baseItem.getId());
|
||||
if (noHistory) songListIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
songListIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
|
||||
activity.startActivity(songListIntent);
|
||||
|
||||
return;
|
||||
|
||||
case "Audio":
|
||||
case Audio:
|
||||
//produce item menu
|
||||
KeyProcessor.HandleKey(KeyEvent.KEYCODE_MENU, rowItem, (BaseActivity) activity);
|
||||
return;
|
||||
|
||||
case "Season":
|
||||
case "RecordingGroup":
|
||||
case Season:
|
||||
case RecordingGroup:
|
||||
//Start activity for enhanced browse
|
||||
Intent seasonIntent = new Intent(activity, GenericFolderActivity.class);
|
||||
seasonIntent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(baseItem));
|
||||
if (noHistory) seasonIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
seasonIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
|
||||
activity.startActivity(seasonIntent);
|
||||
|
||||
return;
|
||||
|
||||
case "BoxSet":
|
||||
case BoxSet:
|
||||
// open collection browsing
|
||||
Intent collectionIntent = new Intent(activity, CollectionActivity.class);
|
||||
collectionIntent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(baseItem));
|
||||
if (noHistory) collectionIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
collectionIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
|
||||
activity.startActivity(collectionIntent);
|
||||
return;
|
||||
|
||||
case "Photo":
|
||||
case Photo:
|
||||
// open photo player
|
||||
MediaManager.setCurrentMediaPosition(pos);
|
||||
Intent photoIntent = new Intent(activity, PhotoPlayerActivity.class);
|
||||
@@ -174,7 +178,9 @@ public class ItemLauncher {
|
||||
public void onResponse(DisplayPreferences response) {
|
||||
Intent intent = new Intent(activity, GenericGridActivity.class);
|
||||
intent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(baseItem));
|
||||
if (noHistory) intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
|
||||
activity.startActivity(intent);
|
||||
|
||||
@@ -187,16 +193,18 @@ public class ItemLauncher {
|
||||
//Start details fragment for display and playback
|
||||
Intent intent = new Intent(activity, FullDetailsActivity.class);
|
||||
intent.putExtra("ItemId", baseItem.getId());
|
||||
if (noHistory) intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
activity.startActivity(intent);
|
||||
break;
|
||||
case Play:
|
||||
if (baseItem.getPlayAccess() == PlayAccess.Full) {
|
||||
//Just play it directly
|
||||
PlaybackHelper.getItemsToPlay(baseItem, baseItem.getType().equals("Movie"), false, new Response<List<BaseItemDto>>() {
|
||||
PlaybackHelper.getItemsToPlay(baseItem, baseItem.getBaseItemType() == BaseItemType.Movie, false, new Response<List<BaseItemDto>>() {
|
||||
@Override
|
||||
public void onResponse(List<BaseItemDto> response) {
|
||||
Intent intent = new Intent(activity,application.getPlaybackActivityClass(baseItem.getType()));
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(baseItem.getBaseItemType()));
|
||||
MediaManager.setCurrentVideoQueue(response);
|
||||
intent.putExtra("Position", 0);
|
||||
activity.startActivity(intent);
|
||||
@@ -213,7 +221,9 @@ public class ItemLauncher {
|
||||
//Start details fragment
|
||||
Intent intent = new Intent(activity, FullDetailsActivity.class);
|
||||
intent.putExtra("ItemId", rowItem.getPerson().getId());
|
||||
if (noHistory) intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
if (noHistory) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
|
||||
activity.startActivity(intent);
|
||||
|
||||
@@ -221,13 +231,13 @@ public class ItemLauncher {
|
||||
case Chapter:
|
||||
final ChapterItemInfo chapter = rowItem.getChapterInfo();
|
||||
//Start playback of the item at the chapter point
|
||||
application.getApiClient().GetItemAsync(chapter.getItemId(), application.getCurrentUser().getId(), new Response<BaseItemDto>(){
|
||||
application.getApiClient().GetItemAsync(chapter.getItemId(), application.getCurrentUser().getId(), new Response<BaseItemDto>() {
|
||||
@Override
|
||||
public void onResponse(BaseItemDto response) {
|
||||
List<BaseItemDto> items = new ArrayList<>();
|
||||
items.add(response);
|
||||
MediaManager.setCurrentVideoQueue(items);
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(response.getType()));
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(response.getBaseItemType()));
|
||||
Long start = chapter.getStartPositionTicks() / 10000;
|
||||
intent.putExtra("Position", start.intValue());
|
||||
activity.startActivity(intent);
|
||||
@@ -237,7 +247,7 @@ public class ItemLauncher {
|
||||
break;
|
||||
case Server:
|
||||
//Log in to selected server
|
||||
ServerSignIn(application.getConnectionManager(), rowItem.getServerInfo(), activity);
|
||||
AuthenticationHelper.signInToServer(application.getConnectionManager(), rowItem.getServerInfo().getAddress(), activity);
|
||||
break;
|
||||
|
||||
case User:
|
||||
@@ -256,14 +266,14 @@ public class ItemLauncher {
|
||||
application.getApiClient().GetItemAsync(hint.getItemId(), application.getCurrentUser().getId(), new Response<BaseItemDto>() {
|
||||
@Override
|
||||
public void onResponse(BaseItemDto response) {
|
||||
if ((response.getIsFolderItem() && !"Series".equals(response.getType())) || "MusicArtist".equals(response.getType())) {
|
||||
if ((response.getIsFolderItem() && response.getBaseItemType() != BaseItemType.Series) || response.getBaseItemType() == BaseItemType.MusicArtist) {
|
||||
// open generic folder browsing
|
||||
Intent intent = new Intent(activity, GenericGridActivity.class);
|
||||
intent.putExtra("Folder", TvApp.getApplication().getSerializer().SerializeToString(response));
|
||||
|
||||
activity.startActivity(intent);
|
||||
|
||||
} else if ("Audio".equals(response.getType())) {
|
||||
} else if (response.getBaseItemType() == BaseItemType.Audio) {
|
||||
PlaybackHelper.retrieveAndPlay(response.getId(), false, activity);
|
||||
//produce item menu
|
||||
// KeyProcessor.HandleKey(KeyEvent.KEYCODE_MENU, rowItem, (BaseActivity) activity);
|
||||
@@ -272,8 +282,10 @@ public class ItemLauncher {
|
||||
} else {
|
||||
Intent intent = new Intent(activity, FullDetailsActivity.class);
|
||||
intent.putExtra("ItemId", response.getId());
|
||||
if (response.getType().equals("Program")) {
|
||||
intent.putExtra("ItemType", response.getType());
|
||||
if (response.getBaseItemType() == BaseItemType.Program) {
|
||||
// TODO: Seems like this is never used...
|
||||
intent.putExtra("ItemType", response.getBaseItemType().name());
|
||||
|
||||
intent.putExtra("ChannelId", response.getChannelId());
|
||||
intent.putExtra("ProgramInfo", TvApp.getApplication().getSerializer().SerializeToString(response));
|
||||
}
|
||||
@@ -296,11 +308,14 @@ public class ItemLauncher {
|
||||
//Start details fragment for display and playback
|
||||
Intent programIntent = new Intent(activity, FullDetailsActivity.class);
|
||||
programIntent.putExtra("ItemId", program.getId());
|
||||
programIntent.putExtra("ItemType", program.getType());
|
||||
|
||||
// TODO Seems unused
|
||||
programIntent.putExtra("ItemType", program.getBaseItemType().name());
|
||||
|
||||
programIntent.putExtra("ChannelId", program.getChannelId());
|
||||
programIntent.putExtra("ProgramInfo", TvApp.getApplication().getSerializer().SerializeToString(program));
|
||||
|
||||
activity.startActivity(programIntent);
|
||||
activity.startActivity(programIntent);
|
||||
break;
|
||||
case Play:
|
||||
if (program.getPlayAccess() == PlayAccess.Full) {
|
||||
@@ -310,7 +325,7 @@ public class ItemLauncher {
|
||||
public void onResponse(BaseItemDto response) {
|
||||
List<BaseItemDto> items = new ArrayList<>();
|
||||
items.add(response);
|
||||
Intent intent = new Intent(activity, TvApp.getApplication().getPlaybackActivityClass(response.getType()));
|
||||
Intent intent = new Intent(activity, TvApp.getApplication().getPlaybackActivityClass(response.getBaseItemType()));
|
||||
MediaManager.setCurrentVideoQueue(items);
|
||||
intent.putExtra("Position", 0);
|
||||
activity.startActivity(intent);
|
||||
@@ -332,7 +347,8 @@ public class ItemLauncher {
|
||||
PlaybackHelper.getItemsToPlay(response, false, false, new Response<List<BaseItemDto>>() {
|
||||
@Override
|
||||
public void onResponse(List<BaseItemDto> response) {
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(channel.getType()));
|
||||
// TODO Check whether this usage of BaseItemType.valueOf is okay.
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(BaseItemType.valueOf(channel.getType())));
|
||||
MediaManager.setCurrentVideoQueue(response);
|
||||
intent.putExtra("Position", 0);
|
||||
activity.startActivity(intent);
|
||||
@@ -359,7 +375,7 @@ public class ItemLauncher {
|
||||
TvApp.getApplication().getApiClient().GetItemAsync(rowItem.getRecordingInfo().getId(), TvApp.getApplication().getCurrentUser().getId(), new Response<BaseItemDto>() {
|
||||
@Override
|
||||
public void onResponse(BaseItemDto response) {
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(rowItem.getType()));
|
||||
Intent intent = new Intent(activity, application.getPlaybackActivityClass(rowItem.getBaseItemType()));
|
||||
List<BaseItemDto> items = new ArrayList<>();
|
||||
items.add(response);
|
||||
MediaManager.setCurrentVideoQueue(items);
|
||||
@@ -433,63 +449,8 @@ public class ItemLauncher {
|
||||
Intent schedIntent = new Intent(activity, BrowseScheduleActivity.class);
|
||||
activity.startActivity(schedIntent);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void ServerSignIn(final IConnectionManager connectionManager, final ServerInfo serverInfo, final Activity activity) {
|
||||
//Connect to the selected server
|
||||
final DelayedMessage message = new DelayedMessage(activity);
|
||||
connectionManager.Connect(serverInfo, new Response<ConnectionResult>() {
|
||||
@Override
|
||||
public void onResponse(ConnectionResult response) {
|
||||
message.Cancel();
|
||||
switch (response.getState()) {
|
||||
case Unavailable:
|
||||
Utils.showToast(activity, "Server unavailable");
|
||||
break;
|
||||
case SignedIn:
|
||||
if (serverInfo.getUserLinkType() != null) {
|
||||
// go straight in for connect only
|
||||
response.getApiClient().GetUserAsync(serverInfo.getUserId(), new Response<UserDto>() {
|
||||
@Override
|
||||
public void onResponse(UserDto response) {
|
||||
TvApp.getApplication().setCurrentUser(response);
|
||||
Intent homeIntent = new Intent(activity, MainActivity.class);
|
||||
activity.startActivity(homeIntent);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ServerSignIn:
|
||||
//Set api client for login
|
||||
TvApp.getApplication().setLoginApiClient(response.getApiClient());
|
||||
//Open user selection
|
||||
Intent userIntent = new Intent(activity, SelectUserActivity.class);
|
||||
userIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
activity.startActivity(userIntent);
|
||||
break;
|
||||
case ConnectSignIn:
|
||||
case ServerSelection:
|
||||
Utils.showToast(activity, "Unexpected response from server connect: " + response.getState());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
message.Cancel();
|
||||
Utils.showToast(activity, "Error Signing in to server");
|
||||
exception.printStackTrace();
|
||||
Utils.reportError(activity, "Error Signing in to server");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package org.jellyfin.androidtv.itemhandling;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.PresenterSelector;
|
||||
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.PresenterSelector;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
@@ -37,6 +38,7 @@ import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.apiclient.ServerInfo;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemPerson;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
import org.jellyfin.apiclient.model.livetv.ChannelInfoDto;
|
||||
import org.jellyfin.apiclient.model.livetv.LiveTvChannelQuery;
|
||||
@@ -62,9 +64,6 @@ import org.jellyfin.apiclient.model.search.SearchHint;
|
||||
import org.jellyfin.apiclient.model.search.SearchHintResult;
|
||||
import org.jellyfin.apiclient.model.search.SearchQuery;
|
||||
|
||||
/**
|
||||
* Created by Eric on 12/5/2014.
|
||||
*/
|
||||
public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
private ItemQuery mQuery;
|
||||
private NextUpQuery mNextUpQuery;
|
||||
@@ -90,7 +89,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
private EmptyResponse mRetrieveStartedListener;
|
||||
private EmptyResponse mRetrieveFinishedListener;
|
||||
|
||||
private ChangeTriggerType[] reRetrieveTriggers = new ChangeTriggerType[] {};
|
||||
private ChangeTriggerType[] reRetrieveTriggers = new ChangeTriggerType[]{};
|
||||
private Calendar lastFullRetrieve;
|
||||
|
||||
private BaseItemPerson[] mPersons;
|
||||
@@ -123,11 +122,21 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getPreferParentThumb() { return preferParentThumb; }
|
||||
public boolean isStaticHeight() { return staticHeight; }
|
||||
public QueryType getQueryType() { return queryType; }
|
||||
public boolean getPreferParentThumb() {
|
||||
return preferParentThumb;
|
||||
}
|
||||
|
||||
public ArrayObjectAdapter getParent() { return mParent; }
|
||||
public boolean isStaticHeight() {
|
||||
return staticHeight;
|
||||
}
|
||||
|
||||
public QueryType getQueryType() {
|
||||
return queryType;
|
||||
}
|
||||
|
||||
public ArrayObjectAdapter getParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
public void setRow(ListRow row) {
|
||||
mRow = row;
|
||||
@@ -136,6 +145,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void setReRetrieveTriggers(ChangeTriggerType[] reRetrieveTriggers) {
|
||||
this.reRetrieveTriggers = reRetrieveTriggers;
|
||||
}
|
||||
|
||||
public ItemRowAdapter(ItemQuery query, int chunkSize, boolean preferParentThumb, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
this(query, chunkSize, preferParentThumb, false, presenter, parent);
|
||||
}
|
||||
@@ -148,7 +158,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
this.chunkSize = chunkSize;
|
||||
this.preferParentThumb = preferParentThumb;
|
||||
this.staticHeight = staticHeight;
|
||||
if (chunkSize > 0) mQuery.setLimit(chunkSize);
|
||||
if (chunkSize > 0) {
|
||||
mQuery.setLimit(chunkSize);
|
||||
}
|
||||
this.queryType = queryType;
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
@@ -161,7 +173,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
this.chunkSize = chunkSize;
|
||||
this.preferParentThumb = preferParentThumb;
|
||||
this.staticHeight = staticHeight;
|
||||
if (chunkSize > 0) mQuery.setLimit(chunkSize);
|
||||
if (chunkSize > 0) {
|
||||
mQuery.setLimit(chunkSize);
|
||||
}
|
||||
this.queryType = queryType;
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
@@ -177,7 +191,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mArtistsQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
staticHeight = true;
|
||||
this.chunkSize = chunkSize;
|
||||
if (chunkSize > 0) mArtistsQuery.setLimit(chunkSize);
|
||||
if (chunkSize > 0) {
|
||||
mArtistsQuery.setLimit(chunkSize);
|
||||
}
|
||||
queryType = QueryType.AlbumArtists;
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
@@ -190,7 +206,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
queryType = QueryType.NextUp;
|
||||
this.preferParentThumb = preferParentThumb;
|
||||
this.staticHeight = true;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(SeriesTimerQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -198,7 +214,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mParent = parent;
|
||||
mSeriesTimerQuery = query;
|
||||
queryType = QueryType.SeriesTimer;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(LatestItemsQuery query, boolean preferParentThumb, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -208,7 +224,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mLatestQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
queryType = QueryType.LatestItems;
|
||||
this.preferParentThumb = preferParentThumb;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(BaseItemPerson[] people, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -253,7 +269,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mParent = parent;
|
||||
mSpecialsQuery = query;
|
||||
queryType = QueryType.Specials;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(TrailersQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -261,7 +277,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mParent = parent;
|
||||
mTrailersQuery = query;
|
||||
queryType = QueryType.Trailers;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(LiveTvChannelQuery query, int chunkSize, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -269,9 +285,11 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mParent = parent;
|
||||
mTvChannelQuery = query;
|
||||
this.chunkSize = chunkSize;
|
||||
if (chunkSize > 0) mTvChannelQuery.setLimit(chunkSize);
|
||||
if (chunkSize > 0) {
|
||||
mTvChannelQuery.setLimit(chunkSize);
|
||||
}
|
||||
queryType = QueryType.LiveTvChannel;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(RecommendedProgramQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -280,7 +298,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mTvProgramQuery = query;
|
||||
queryType = QueryType.LiveTvProgram;
|
||||
staticHeight = true;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(RecordingQuery query, int chunkSize, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -290,7 +308,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
this.chunkSize = chunkSize;
|
||||
queryType = QueryType.LiveTvRecording;
|
||||
staticHeight = true;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(RecordingGroupQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -298,7 +316,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mParent = parent;
|
||||
mTvRecordingGroupQuery = query;
|
||||
queryType = QueryType.LiveTvRecordingGroup;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(SimilarItemsQuery query, QueryType queryType, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -307,7 +325,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mSimilarQuery = query;
|
||||
mSimilarQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
this.queryType = queryType;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(UpcomingEpisodesQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -316,7 +334,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mUpcomingQuery = query;
|
||||
mUpcomingQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
queryType = QueryType.Upcoming;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(SeasonQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -325,7 +343,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mSeasonQuery = query;
|
||||
mSeasonQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
queryType = QueryType.Season;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(PersonsQuery query, int chunkSize, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -334,9 +352,11 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
this.chunkSize = chunkSize;
|
||||
mPersonsQuery = query;
|
||||
mPersonsQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
if (chunkSize > 0) mPersonsQuery.setLimit(chunkSize);
|
||||
if (chunkSize > 0) {
|
||||
mPersonsQuery.setLimit(chunkSize);
|
||||
}
|
||||
queryType = QueryType.Persons;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(SearchQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -346,7 +366,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mSearchQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
|
||||
mSearchQuery.setLimit(50);
|
||||
queryType = QueryType.Search;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(ViewQuery query, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -354,7 +374,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mParent = parent;
|
||||
queryType = QueryType.Views;
|
||||
staticHeight = true;
|
||||
add(new BaseRowItem(new GridButton(0,TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
add(new BaseRowItem(new GridButton(0, TvApp.getApplication().getString(R.string.lbl_loading_elipses), R.drawable.loading)));
|
||||
}
|
||||
|
||||
public ItemRowAdapter(ServerInfo serverInfo, Presenter presenter, ArrayObjectAdapter parent) {
|
||||
@@ -383,7 +403,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
totalItems = amt;
|
||||
}
|
||||
|
||||
public int getTotalItems() { return totalItems; }
|
||||
public int getTotalItems() {
|
||||
return totalItems;
|
||||
}
|
||||
|
||||
public void setSortBy(HorizontalGridFragment.SortOption option) {
|
||||
if (!option.value.equals(mSortBy)) {
|
||||
@@ -394,53 +416,69 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
mArtistsQuery.setSortOrder(option.order);
|
||||
break;
|
||||
default:
|
||||
mQuery.setSortBy(new String[] {mSortBy});
|
||||
mQuery.setSortBy(new String[]{mSortBy});
|
||||
mQuery.setSortOrder(option.order);
|
||||
break;
|
||||
}
|
||||
if (!"SortName".equals(option.value)) setStartLetter(null);
|
||||
if (!"SortName".equals(option.value)) {
|
||||
setStartLetter(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BaseRowItem findByIndex(int ndx) {
|
||||
//search for actual index number and return matching item
|
||||
for (int i = 0; i < getItemsLoaded(); i++) {
|
||||
BaseRowItem item = (BaseRowItem)this.get(i);
|
||||
if (item.getIndex() == ndx) return item;
|
||||
BaseRowItem item = (BaseRowItem) this.get(i);
|
||||
if (item.getIndex() == ndx) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSortBy() { return mSortBy; }
|
||||
public String getSortBy() {
|
||||
return mSortBy;
|
||||
}
|
||||
|
||||
public FilterOptions getFilters() { return mFilters; }
|
||||
public FilterOptions getFilters() {
|
||||
return mFilters;
|
||||
}
|
||||
|
||||
public void setFilters(FilterOptions filters) {
|
||||
mFilters = filters;
|
||||
switch (queryType) {
|
||||
case AlbumArtists:
|
||||
mArtistsQuery.setFilters(mFilters != null ? mFilters.getFilters() : null);
|
||||
break;
|
||||
default:
|
||||
mQuery.setFilters(mFilters != null ? mFilters.getFilters() : null);
|
||||
}
|
||||
switch (queryType) {
|
||||
case AlbumArtists:
|
||||
mArtistsQuery.setFilters(mFilters != null ? mFilters.getFilters() : null);
|
||||
break;
|
||||
default:
|
||||
mQuery.setFilters(mFilters != null ? mFilters.getFilters() : null);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPosition(int pos) {
|
||||
((IPositionablePresenter)(getParent().getPresenter(this))).setPosition(pos);
|
||||
((IPositionablePresenter) (getParent().getPresenter(this))).setPosition(pos);
|
||||
}
|
||||
|
||||
public String getStartLetter() { return mQuery != null ? mQuery.getNameStartsWithOrGreater() : null; }
|
||||
public String getStartLetter() {
|
||||
return mQuery != null ? mQuery.getNameStartsWithOrGreater() : null;
|
||||
}
|
||||
|
||||
public void setStartLetter(String value) {
|
||||
switch (queryType) {
|
||||
case AlbumArtists:
|
||||
if (value != null && value.equals("#")) mArtistsQuery.setNameStartsWithOrGreater(null);
|
||||
else mArtistsQuery.setNameStartsWithOrGreater(value);
|
||||
if (value != null && value.equals("#")) {
|
||||
mArtistsQuery.setNameStartsWithOrGreater(null);
|
||||
} else {
|
||||
mArtistsQuery.setNameStartsWithOrGreater(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (value != null && value.equals("#")) mQuery.setNameStartsWithOrGreater(null);
|
||||
else mQuery.setNameStartsWithOrGreater(value);
|
||||
if (value != null && value.equals("#")) {
|
||||
mQuery.setNameStartsWithOrGreater(null);
|
||||
} else {
|
||||
mQuery.setNameStartsWithOrGreater(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -450,7 +488,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
// just clear us
|
||||
clear();
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
if (mParent.size() == 1) {
|
||||
// we will be removing the last row - show something and prevent the framework from crashing
|
||||
@@ -483,7 +521,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void RetrieveNext() {
|
||||
switch (queryType) {
|
||||
case Persons:
|
||||
if (fullyLoaded || mPersonsQuery == null || isCurrentlyRetrieving()) return;
|
||||
if (fullyLoaded || mPersonsQuery == null || isCurrentlyRetrieving()) {
|
||||
return;
|
||||
}
|
||||
setCurrentlyRetrieving(true);
|
||||
|
||||
//set the query to go get the next chunk
|
||||
@@ -492,7 +532,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
break;
|
||||
|
||||
case LiveTvChannel:
|
||||
if (fullyLoaded || mTvChannelQuery == null || isCurrentlyRetrieving()) return;
|
||||
if (fullyLoaded || mTvChannelQuery == null || isCurrentlyRetrieving()) {
|
||||
return;
|
||||
}
|
||||
setCurrentlyRetrieving(true);
|
||||
|
||||
//set the query to go get the next chunk
|
||||
@@ -501,7 +543,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
break;
|
||||
|
||||
case AlbumArtists:
|
||||
if (fullyLoaded || mArtistsQuery == null || isCurrentlyRetrieving()) return;
|
||||
if (fullyLoaded || mArtistsQuery == null || isCurrentlyRetrieving()) {
|
||||
return;
|
||||
}
|
||||
setCurrentlyRetrieving(true);
|
||||
|
||||
//set the query to go get the next chunk
|
||||
@@ -510,7 +554,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
break;
|
||||
|
||||
default:
|
||||
if (fullyLoaded || mQuery == null || isCurrentlyRetrieving()) return;
|
||||
if (fullyLoaded || mQuery == null || isCurrentlyRetrieving()) {
|
||||
return;
|
||||
}
|
||||
setCurrentlyRetrieving(true);
|
||||
|
||||
//set the query to go get the next chunk
|
||||
@@ -521,7 +567,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
|
||||
public boolean ReRetrieveIfNeeded() {
|
||||
if (reRetrieveTriggers == null) return false;
|
||||
if (reRetrieveTriggers == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean retrieve = false;
|
||||
TvApp app = TvApp.getApplication();
|
||||
@@ -558,7 +606,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
|
||||
if (retrieve) {
|
||||
TvApp.getApplication().getLogger().Info("Re-retrieving row of type "+ queryType);
|
||||
TvApp.getApplication().getLogger().Info("Re-retrieving row of type " + queryType);
|
||||
Retrieve();
|
||||
}
|
||||
|
||||
@@ -665,7 +713,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
totalItems = response.length;
|
||||
setItemsLoaded(totalItems);
|
||||
if (totalItems == 0) removeRow();
|
||||
if (totalItems == 0) {
|
||||
removeRow();
|
||||
}
|
||||
|
||||
currentlyRetrieving = false;
|
||||
}
|
||||
@@ -748,7 +798,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
currentlyRetrieving = false;
|
||||
}
|
||||
|
||||
private static String[] ignoreTypes = new String[] {"books","games"};
|
||||
private static String[] ignoreTypes = new String[]{"books", "games"};
|
||||
private static List<String> ignoreTypeList = Arrays.asList(ignoreTypes);
|
||||
|
||||
private void RetrieveViews() {
|
||||
@@ -763,8 +813,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
//re-map the display prefs id to our actual id
|
||||
item.setDisplayPreferencesId(item.getId());
|
||||
if (!ignoreTypeList.contains(item.getCollectionType()) && !ignoreTypeList.contains(item.getType()))
|
||||
if (!ignoreTypeList.contains(item.getCollectionType())) {
|
||||
adapter.add(new BaseRowItem(i++, item, preferParentThumb, staticHeight));
|
||||
}
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
@@ -799,7 +850,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(SearchHintResult response) {
|
||||
if (response.getSearchHints() != null && response.getSearchHints().length > 0) {
|
||||
int i = 0;
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (SearchHint item : response.getSearchHints()) {
|
||||
if (!ignoreTypeList.contains(item.getType())) {
|
||||
i++;
|
||||
@@ -808,10 +861,10 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
if (itemsLoaded > 0 && mParent != null) mParent.add(mRow);
|
||||
}
|
||||
|
||||
currentlyRetrieving = false;
|
||||
notifyRetrieveFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -819,11 +872,18 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
TvApp.getApplication().getLogger().ErrorException("Error retrieving search results", exception);
|
||||
Utils.showToast(TvApp.getApplication(), exception.getLocalizedMessage());
|
||||
currentlyRetrieving = false;
|
||||
notifyRetrieveFinished();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void addToParentIfResultsReceived() {
|
||||
if (itemsLoaded > 0 && mParent != null) {
|
||||
mParent.add(mRow);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResultSizeAsync(final Response<Integer> outerResponse) {
|
||||
switch (queryType) {
|
||||
case AlbumArtists:
|
||||
@@ -895,7 +955,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
if (getItemsLoaded() == 0) removeRow();
|
||||
if (getItemsLoaded() == 0) {
|
||||
removeRow();
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentlyRetrieving(false);
|
||||
@@ -934,7 +996,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void RetrieveAudioPlaylists(final ItemQuery query) {
|
||||
//Add specialized playlists first
|
||||
clear();
|
||||
add(new GridButton(EnhancedBrowseFragment.FAVSONGS, TvApp.getApplication().getString(R.string.lbl_favorites), R.drawable.genericmusic));
|
||||
add(new GridButton(EnhancedBrowseFragment.FAVSONGS, TvApp.getApplication().getString(R.string.lbl_favorites), R.drawable.favorites));
|
||||
itemsLoaded = 1;
|
||||
Retrieve(query);
|
||||
}
|
||||
@@ -944,7 +1006,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
clear();
|
||||
if (MediaManager.hasVideoQueueItems()) {
|
||||
TvApp.getApplication().getLogger().Debug("Adding video queue...");
|
||||
add(new BaseRowItem(new GridButton(TvApp.VIDEO_QUEUE_OPTION_ID, TvApp.getApplication().getString(R.string.lbl_current_queue), R.drawable.videoqueue)));
|
||||
add(new BaseRowItem(new GridButton(TvApp.VIDEO_QUEUE_OPTION_ID, TvApp.getApplication().getString(R.string.lbl_current_queue), R.drawable.tile_port_video_queue)));
|
||||
itemsLoaded = 1;
|
||||
}
|
||||
Retrieve(query);
|
||||
@@ -1029,7 +1091,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
TvApp.getApplication().getApiClient().GetItemsAsync(query, new Response<ItemsResult>() {
|
||||
@Override
|
||||
public void onResponse(ItemsResult response) {
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = 0;
|
||||
Calendar compare = Calendar.getInstance();
|
||||
@@ -1038,7 +1102,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
if (item.getIndexNumber() != null && item.getIndexNumber() == 1 && (item.getDateCreated() == null || item.getDateCreated().after(compare.getTime()))
|
||||
&& (item.getUserData() == null || item.getUserData().getLikes() == null || item.getUserData().getLikes())
|
||||
) {
|
||||
) {
|
||||
// new unwatched episode 1 not disliked - check to be sure prev episode not already in next up
|
||||
BaseItemDto nextUpItem = null;
|
||||
for (int n = 0; n < nextUpItems.length; n++) {
|
||||
@@ -1053,8 +1117,8 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
BaseRowItem existing = null;
|
||||
int existingPos = -1;
|
||||
for (int n = 0; n < adapter.size(); n++) {
|
||||
if (((BaseRowItem)adapter.get(n)).getBaseItem().getSeriesId().equals(item.getSeriesId())) {
|
||||
existing = (BaseRowItem)adapter.get(n);
|
||||
if (((BaseRowItem) adapter.get(n)).getBaseItem().getSeriesId().equals(item.getSeriesId())) {
|
||||
existing = (BaseRowItem) adapter.get(n);
|
||||
existingPos = n;
|
||||
break;
|
||||
}
|
||||
@@ -1078,7 +1142,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
}
|
||||
|
||||
|
||||
if (adapter.size() == 0) removeRow();
|
||||
if (adapter.size() == 0) {
|
||||
removeRow();
|
||||
}
|
||||
currentlyRetrieving = false;
|
||||
}
|
||||
});
|
||||
@@ -1095,7 +1161,9 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
@Override
|
||||
public void onResponse(final ItemsResult response) {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
int i = 0;
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
adapter.add(new BaseRowItem(i++, item, preferParentThumb, staticHeight));
|
||||
@@ -1108,7 +1176,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
} else {
|
||||
//If this was for a single series, get the rest of the episodes in the season
|
||||
if (query.getSeriesId() != null) {
|
||||
BaseItemDto first = adapter.size() == 1 ? ((BaseRowItem)adapter.get(0)).getBaseItem() : null;
|
||||
BaseItemDto first = adapter.size() == 1 ? ((BaseRowItem) adapter.get(0)).getBaseItem() : null;
|
||||
if (first != null && first.getIndexNumber() != null && first.getSeasonId() != null) {
|
||||
StdItemQuery rest = new StdItemQuery();
|
||||
rest.setUserId(query.getUserId());
|
||||
@@ -1165,14 +1233,18 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(ChannelInfoDtoResult response) {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = itemsLoaded;
|
||||
if (i == 0 && adapter.size() > 0) adapter.clear();
|
||||
if (i == 0 && adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (ChannelInfoDto item : response.getItems()) {
|
||||
adapter.add(new BaseRowItem(i, item));
|
||||
i++;
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
@@ -1201,11 +1273,6 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = 0;
|
||||
int prevItems = adapter.size() > 0 ? adapter.size() : 0;
|
||||
if (query.getIsAiring()) {
|
||||
// show guide option as first item
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_GUIDE_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_live_tv_guide), R.drawable.guide)));
|
||||
i++;
|
||||
}
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
adapter.add(new BaseRowItem(item, staticHeight));
|
||||
i++;
|
||||
@@ -1248,7 +1315,7 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
int i = 0;
|
||||
int prevItems = adapter.size() > 0 ? adapter.size() : 0;
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
item.setType("RecordingGroup"); // the API does not fill this in
|
||||
item.setBaseItemType(BaseItemType.RecordingGroup); // the API does not fill this in
|
||||
item.setIsFolder(true); // nor this
|
||||
adapter.add(new BaseRowItem(item));
|
||||
i++;
|
||||
@@ -1331,14 +1398,14 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
int prevItems = adapter.size() > 0 ? adapter.size() : 0;
|
||||
if (adapter.chunkSize == 0) {
|
||||
// and recordings as first item if showing all
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_RECORDINGS_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_recorded_tv), R.drawable.recgroup)));
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_RECORDINGS_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_recorded_tv), R.drawable.tile_port_record)));
|
||||
i++;
|
||||
if (TvApp.getApplication().canManageRecordings()) {
|
||||
// and schedule
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_SCHEDULE_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_schedule), R.drawable.clock)));
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_SCHEDULE_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_schedule), R.drawable.tile_port_time)));
|
||||
i++;
|
||||
// and series
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_SERIES_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_series), R.drawable.seriestimerp)));
|
||||
adapter.add(new BaseRowItem(new GridButton(TvApp.LIVE_TV_SERIES_OPTION_ID, TvApp.getApplication().getResources().getString(R.string.lbl_series), R.drawable.tile_port_series_timer)));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -1382,13 +1449,17 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(BaseItemDto[] response) {
|
||||
if (response.length > 0) {
|
||||
int i = 0;
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (BaseItemDto item : response) {
|
||||
adapter.add(new BaseRowItem(i++, item, preferParentThumb, false));
|
||||
}
|
||||
totalItems = response.length;
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
@@ -1415,14 +1486,18 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(BaseItemDto[] response) {
|
||||
if (response.length > 0) {
|
||||
int i = 0;
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (BaseItemDto item : response) {
|
||||
item.setName(TvApp.getApplication().getString(R.string.lbl_trailer) + (i + 1));
|
||||
adapter.add(new BaseRowItem(i++, item, preferParentThumb, false, BaseRowItem.SelectAction.Play));
|
||||
}
|
||||
totalItems = response.length;
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
@@ -1449,13 +1524,17 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(ItemsResult response) {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = 0;
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
adapter.add(new BaseRowItem(i++, item));
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
@@ -1482,13 +1561,17 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(ItemsResult response) {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = 0;
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
adapter.add(new BaseRowItem(i++, item));
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
@@ -1515,14 +1598,19 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(ItemsResult response) {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = 0;
|
||||
if (adapter.size() > 0) adapter.clear();
|
||||
if (adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
if (query.getParentId() == null || item.getSeriesId() == null || item.getSeriesId().equals(query.getParentId()))
|
||||
if (query.getParentId() == null || item.getSeriesId() == null || item.getSeriesId().equals(query.getParentId())) {
|
||||
adapter.add(new BaseRowItem(i++, item));
|
||||
}
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(itemsLoaded + i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
@@ -1549,13 +1637,17 @@ public class ItemRowAdapter extends ArrayObjectAdapter {
|
||||
public void onResponse(ItemsResult response) {
|
||||
if (response.getItems() != null && response.getItems().length > 0) {
|
||||
int i = itemsLoaded;
|
||||
if (i == 0 && adapter.size() > 0) adapter.clear();
|
||||
if (i == 0 && adapter.size() > 0) {
|
||||
adapter.clear();
|
||||
}
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
adapter.add(new BaseRowItem(i++, item));
|
||||
}
|
||||
totalItems = response.getTotalRecordCount();
|
||||
setItemsLoaded(i);
|
||||
if (i == 0) removeRow();
|
||||
if (i == 0) {
|
||||
removeRow();
|
||||
}
|
||||
} else {
|
||||
// no results - don't show us
|
||||
removeRow();
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -425,7 +425,7 @@ public class LiveTvGuideActivity extends BaseActivity implements ILiveTvGuide {
|
||||
mPopup = new PopupWindow(layout, WIDTH, HEIGHT);
|
||||
mPopup.setFocusable(true);
|
||||
mPopup.setOutsideTouchable(true);
|
||||
mPopup.setBackgroundDrawable(new BitmapDrawable()); // necessary for popup to dismiss
|
||||
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // necessary for popup to dismiss
|
||||
mPopup.setAnimationStyle(R.style.PopupSlideInRight);
|
||||
mMovies = layout.findViewById(R.id.movies);
|
||||
mSeries = layout.findViewById(R.id.series);
|
||||
@@ -511,7 +511,7 @@ public class LiveTvGuideActivity extends BaseActivity implements ILiveTvGuide {
|
||||
mPopup = new PopupWindow(layout, WIDTH, HEIGHT);
|
||||
mPopup.setFocusable(true);
|
||||
mPopup.setOutsideTouchable(true);
|
||||
mPopup.setBackgroundDrawable(new BitmapDrawable()); // necessary for popup to dismiss
|
||||
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // necessary for popup to dismiss
|
||||
mPopup.setAnimationStyle(R.style.PopupSlideInRight);
|
||||
mHd = layout.findViewById(R.id.hd);
|
||||
mRepeat = layout.findViewById(R.id.repeat);
|
||||
@@ -925,23 +925,23 @@ public class LiveTvGuideActivity extends BaseActivity implements ILiveTvGuide {
|
||||
InfoLayoutHelper.addInfoRow(mActivity, mSelectedProgram, mInfoRow, false, false);
|
||||
|
||||
if (Utils.isTrue(mSelectedProgram.getIsNews())) {
|
||||
mBackdrop.setImageResource(R.drawable.newsbanner);
|
||||
mBackdrop.setImageResource(R.drawable.banner_news);
|
||||
|
||||
} else if (Utils.isTrue(mSelectedProgram.getIsKids())) {
|
||||
mBackdrop.setImageResource(R.drawable.kidsbanner);
|
||||
mBackdrop.setImageResource(R.drawable.banner_kids);
|
||||
|
||||
} else if (Utils.isTrue(mSelectedProgram.getIsSports())) {
|
||||
mBackdrop.setImageResource(R.drawable.sportsbanner);
|
||||
mBackdrop.setImageResource(R.drawable.banner_sports);
|
||||
|
||||
} else if (Utils.isTrue(mSelectedProgram.getIsMovie())) {
|
||||
mBackdrop.setImageResource(R.drawable.moviebanner);
|
||||
mBackdrop.setImageResource(R.drawable.banner_movie);
|
||||
|
||||
} else {
|
||||
mBackdrop.setImageResource(R.drawable.tvbanner);
|
||||
mBackdrop.setImageResource(R.drawable.banner_tv);
|
||||
}
|
||||
} else {
|
||||
mInfoRow.removeAllViews();
|
||||
mBackdrop.setImageResource(R.drawable.tvbanner);
|
||||
mBackdrop.setImageResource(R.drawable.banner_tv);
|
||||
mImage.setImageResource(R.drawable.blank10x10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package org.jellyfin.androidtv.livetv;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import android.text.format.DateUtils;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
@@ -32,6 +32,7 @@ import java.util.TimeZone;
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.entities.DisplayPreferences;
|
||||
import org.jellyfin.apiclient.model.entities.LocationType;
|
||||
import org.jellyfin.apiclient.model.entities.SortOrder;
|
||||
@@ -405,7 +406,7 @@ public class TvManager {
|
||||
programInfo.setChannelName(timer.getChannelName());
|
||||
programInfo.setName(Utils.getSafeValue(timer.getName(), "Unknown"));
|
||||
TvApp.getApplication().getLogger().Warn("No program info for timer %s. Creating one...", programInfo.getName());
|
||||
programInfo.setType("Program");
|
||||
programInfo.setBaseItemType(BaseItemType.Program);
|
||||
programInfo.setTimerId(timer.getId());
|
||||
programInfo.setSeriesTimerId(timer.getSeriesTimerId());
|
||||
programInfo.setStartDate(timer.getStartDate());
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package org.jellyfin.androidtv.startup;
|
||||
package org.jellyfin.androidtv.model;
|
||||
|
||||
import org.jellyfin.apiclient.model.apiclient.ServerInfo;
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
|
||||
/**
|
||||
* Created by Eric on 1/20/2015.
|
||||
*/
|
||||
public class LogonCredentials {
|
||||
private ServerInfo serverInfo;
|
||||
private UserDto userDto;
|
||||
@@ -30,5 +27,4 @@ public class LogonCredentials {
|
||||
public void setUserDto(UserDto userDto) {
|
||||
this.userDto = userDto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,10 @@ package org.jellyfin.androidtv.model.compat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.jellyfin.androidtv.constants.CodecTypes;
|
||||
import org.jellyfin.androidtv.constants.ContainerTypes;
|
||||
import org.jellyfin.androidtv.constants.MediaTypes;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecType;
|
||||
import org.jellyfin.apiclient.model.dlna.DeviceProfile;
|
||||
@@ -16,7 +20,6 @@ import org.jellyfin.apiclient.model.dlna.ResponseProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.SubtitleDeliveryMethod;
|
||||
import org.jellyfin.apiclient.model.dlna.SubtitleProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.TranscodingProfile;
|
||||
import org.jellyfin.apiclient.model.extensions.StringHelper;
|
||||
|
||||
@Deprecated
|
||||
public class AndroidProfile extends DeviceProfile {
|
||||
@@ -40,50 +43,50 @@ public class AndroidProfile extends DeviceProfile {
|
||||
ArrayList<TranscodingProfile> transcodingProfiles = new ArrayList<>();
|
||||
|
||||
TranscodingProfile tempVar = new TranscodingProfile();
|
||||
tempVar.setContainer("mp3");
|
||||
tempVar.setAudioCodec("mp3");
|
||||
tempVar.setContainer(CodecTypes.MP3);
|
||||
tempVar.setAudioCodec(CodecTypes.MP3);
|
||||
tempVar.setType(DlnaProfileType.Audio);
|
||||
tempVar.setContext(EncodingContext.Streaming);
|
||||
transcodingProfiles.add(tempVar);
|
||||
|
||||
TranscodingProfile tempVar0 = new TranscodingProfile();
|
||||
tempVar0.setContainer("mp3");
|
||||
tempVar0.setAudioCodec("mp3");
|
||||
tempVar0.setContainer(CodecTypes.MP3);
|
||||
tempVar0.setAudioCodec(CodecTypes.MP3);
|
||||
tempVar0.setType(DlnaProfileType.Audio);
|
||||
tempVar0.setContext(EncodingContext.Static);
|
||||
transcodingProfiles.add(tempVar0);
|
||||
|
||||
if (profileOptions.SupportsHls) {
|
||||
TranscodingProfile tempVar2 = new TranscodingProfile();
|
||||
tempVar2.setProtocol("hls");
|
||||
tempVar2.setContainer("ts");
|
||||
tempVar2.setVideoCodec("h264");
|
||||
tempVar2.setAudioCodec("aac");
|
||||
tempVar2.setProtocol(MediaTypes.HLS);
|
||||
tempVar2.setContainer(ContainerTypes.TS);
|
||||
tempVar2.setVideoCodec(CodecTypes.H264);
|
||||
tempVar2.setAudioCodec(CodecTypes.AAC);
|
||||
tempVar2.setType(DlnaProfileType.Video);
|
||||
tempVar2.setContext(EncodingContext.Streaming);
|
||||
transcodingProfiles.add(tempVar2);
|
||||
}
|
||||
TranscodingProfile mkvProfile = new TranscodingProfile();
|
||||
mkvProfile.setContainer("mkv");
|
||||
mkvProfile.setVideoCodec("h264");
|
||||
mkvProfile.setAudioCodec("aac,mp3");
|
||||
mkvProfile.setContainer(ContainerTypes.MKV);
|
||||
mkvProfile.setVideoCodec(CodecTypes.H264);
|
||||
mkvProfile.setAudioCodec(Utils.join(",", CodecTypes.AAC, CodecTypes.MP3));
|
||||
mkvProfile.setType(DlnaProfileType.Video);
|
||||
mkvProfile.setContext(EncodingContext.Streaming);
|
||||
mkvProfile.setCopyTimestamps(true);
|
||||
transcodingProfiles.add(mkvProfile);
|
||||
|
||||
TranscodingProfile tempVar3 = new TranscodingProfile();
|
||||
tempVar3.setContainer("mp4");
|
||||
tempVar3.setVideoCodec("h264");
|
||||
tempVar3.setAudioCodec("aac");
|
||||
tempVar3.setContainer(ContainerTypes.MP4);
|
||||
tempVar3.setVideoCodec(CodecTypes.H264);
|
||||
tempVar3.setAudioCodec(CodecTypes.AAC);
|
||||
tempVar3.setType(DlnaProfileType.Video);
|
||||
tempVar3.setContext(EncodingContext.Static);
|
||||
transcodingProfiles.add(tempVar3);
|
||||
|
||||
TranscodingProfile webmProfile = new TranscodingProfile();
|
||||
webmProfile.setContainer("webm");
|
||||
webmProfile.setVideoCodec("vpx");
|
||||
webmProfile.setAudioCodec("vorbis");
|
||||
webmProfile.setContainer(ContainerTypes.WEBM);
|
||||
webmProfile.setVideoCodec(CodecTypes.VPX);
|
||||
webmProfile.setAudioCodec(CodecTypes.VORBIS);
|
||||
webmProfile.setType(DlnaProfileType.Video);
|
||||
webmProfile.setContext(EncodingContext.Streaming);
|
||||
transcodingProfiles.add(webmProfile);
|
||||
@@ -91,26 +94,31 @@ public class AndroidProfile extends DeviceProfile {
|
||||
setTranscodingProfiles(transcodingProfiles.toArray(new TranscodingProfile[0]));
|
||||
|
||||
DirectPlayProfile tempVar4 = new DirectPlayProfile();
|
||||
tempVar4.setContainer("mp4");
|
||||
tempVar4.setVideoCodec("h264,mpeg4");
|
||||
tempVar4.setAudioCodec("aac");
|
||||
tempVar4.setContainer(ContainerTypes.MP4);
|
||||
tempVar4.setVideoCodec(Utils.join(",", CodecTypes.H264, CodecTypes.MPEG4));
|
||||
tempVar4.setAudioCodec(CodecTypes.AAC);
|
||||
tempVar4.setType(DlnaProfileType.Video);
|
||||
|
||||
DirectPlayProfile tempVar5 = new DirectPlayProfile();
|
||||
tempVar5.setContainer("mp4,aac");
|
||||
tempVar5.setAudioCodec("aac");
|
||||
tempVar5.setContainer(Utils.join(",", ContainerTypes.MP4, CodecTypes.AAC));
|
||||
tempVar5.setAudioCodec(CodecTypes.AAC);
|
||||
tempVar5.setType(DlnaProfileType.Audio);
|
||||
|
||||
DirectPlayProfile tempVar6 = new DirectPlayProfile();
|
||||
tempVar6.setContainer("mp3");
|
||||
tempVar6.setAudioCodec("mp3");
|
||||
tempVar6.setContainer(CodecTypes.MP3);
|
||||
tempVar6.setAudioCodec(CodecTypes.MP3);
|
||||
tempVar6.setType(DlnaProfileType.Audio);
|
||||
|
||||
DirectPlayProfile tempVar7 = new DirectPlayProfile();
|
||||
tempVar7.setContainer("flac");
|
||||
tempVar7.setAudioCodec("flac");
|
||||
tempVar7.setContainer(CodecTypes.FLAC);
|
||||
tempVar7.setAudioCodec(CodecTypes.FLAC);
|
||||
tempVar7.setType(DlnaProfileType.Audio);
|
||||
|
||||
DirectPlayProfile tempVar8 = new DirectPlayProfile();
|
||||
tempVar8.setContainer("ogg");
|
||||
tempVar8.setAudioCodec("vorbis");
|
||||
tempVar8.setContainer(ContainerTypes.OGG);
|
||||
tempVar8.setAudioCodec(CodecTypes.VORBIS);
|
||||
tempVar8.setType(DlnaProfileType.Audio);
|
||||
|
||||
DirectPlayProfile tempVar9 = new DirectPlayProfile();
|
||||
tempVar9.setContainer("jpeg,png,gif,bmp");
|
||||
tempVar9.setType(DlnaProfileType.Photo);
|
||||
@@ -118,7 +126,7 @@ public class AndroidProfile extends DeviceProfile {
|
||||
|
||||
CodecProfile tempVar10 = new CodecProfile();
|
||||
tempVar10.setType(CodecType.Video);
|
||||
tempVar10.setCodec("h264");
|
||||
tempVar10.setCodec(CodecTypes.H264);
|
||||
tempVar10.setConditions(new ProfileCondition[]
|
||||
{
|
||||
new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, profileOptions.DefaultH264Profile),
|
||||
@@ -134,7 +142,7 @@ public class AndroidProfile extends DeviceProfile {
|
||||
|
||||
CodecProfile tempVar11 = new CodecProfile();
|
||||
tempVar11.setType(CodecType.Video);
|
||||
tempVar11.setCodec("vpx,mpeg4");
|
||||
tempVar11.setCodec(Utils.join(",", CodecTypes.VPX, CodecTypes.MPEG4));
|
||||
tempVar11.setConditions(new ProfileCondition[]
|
||||
{
|
||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Width, "1920"),
|
||||
@@ -145,17 +153,17 @@ public class AndroidProfile extends DeviceProfile {
|
||||
|
||||
CodecProfile tempVar12 = new CodecProfile();
|
||||
tempVar12.setType(CodecType.VideoAudio);
|
||||
tempVar12.setCodec("aac");
|
||||
tempVar12.setCodec(CodecTypes.AAC);
|
||||
tempVar12.setConditions(new ProfileCondition[]{new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioChannels, "2")});
|
||||
|
||||
CodecProfile tempVar13 = new CodecProfile();
|
||||
tempVar13.setType(CodecType.Audio);
|
||||
tempVar13.setCodec("aac");
|
||||
tempVar13.setCodec(CodecTypes.AAC);
|
||||
tempVar13.setConditions(new ProfileCondition[]{new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioChannels, "2")});
|
||||
|
||||
CodecProfile tempVar14 = new CodecProfile();
|
||||
tempVar14.setType(CodecType.Audio);
|
||||
tempVar14.setCodec("mp3");
|
||||
tempVar14.setCodec(CodecTypes.MP3);
|
||||
tempVar14.setConditions(new ProfileCondition[]
|
||||
{
|
||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioChannels, "2"),
|
||||
@@ -182,15 +190,15 @@ public class AndroidProfile extends DeviceProfile {
|
||||
for (DirectPlayProfile profile : getDirectPlayProfiles()) {
|
||||
if (profile.getType() == DlnaProfileType.Video) {
|
||||
String container = profile.getContainer();
|
||||
if (container != null && (StringHelper.IndexOfIgnoreCase(container, "mp4") != -1 ||
|
||||
StringHelper.IndexOfIgnoreCase(container, "mkv") != -1 ||
|
||||
StringHelper.IndexOfIgnoreCase(container, "m4v") != -1)) {
|
||||
if (container != null && (container.toLowerCase().contains(ContainerTypes.MP4) ||
|
||||
container.toLowerCase().contains(ContainerTypes.MKV) ||
|
||||
container.toLowerCase().contains(ContainerTypes.M4V))) {
|
||||
|
||||
String audioCodec = profile.getAudioCodec();
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(audioCodec)) {
|
||||
profile.setAudioCodec("ac3");
|
||||
if (Utils.isEmpty(audioCodec)) {
|
||||
profile.setAudioCodec(CodecTypes.AC3);
|
||||
} else {
|
||||
profile.setAudioCodec(audioCodec + ",ac3");
|
||||
profile.setAudioCodec(audioCodec + "," + CodecTypes.AC3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,16 +209,16 @@ public class AndroidProfile extends DeviceProfile {
|
||||
for (DirectPlayProfile profile : getDirectPlayProfiles()) {
|
||||
if (profile.getType() == DlnaProfileType.Video) {
|
||||
String container = profile.getContainer();
|
||||
if (container != null && (StringHelper.IndexOfIgnoreCase(container, "mp4") != -1 ||
|
||||
StringHelper.IndexOfIgnoreCase(container, "mkv") != -1 ||
|
||||
StringHelper.IndexOfIgnoreCase(container, "m4v") != -1 ||
|
||||
StringHelper.IndexOfIgnoreCase(container, "ts") != -1)) {
|
||||
if (container != null && (container.toLowerCase().contains(ContainerTypes.MP4) ||
|
||||
container.toLowerCase().contains(ContainerTypes.MKV) ||
|
||||
container.toLowerCase().contains(ContainerTypes.M4V) ||
|
||||
container.toLowerCase().contains(ContainerTypes.TS))) {
|
||||
|
||||
String audioCodec = profile.getAudioCodec();
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(audioCodec)) {
|
||||
profile.setAudioCodec("dca");
|
||||
if (Utils.isEmpty(audioCodec)) {
|
||||
profile.setAudioCodec(CodecTypes.DCA);
|
||||
} else {
|
||||
profile.setAudioCodec(audioCodec + ",dca");
|
||||
profile.setAudioCodec(audioCodec + "," + CodecTypes.DCA);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,17 +229,18 @@ public class AndroidProfile extends DeviceProfile {
|
||||
for (DirectPlayProfile profile : getDirectPlayProfiles()) {
|
||||
if (profile.getType() == DlnaProfileType.Video) {
|
||||
String container = profile.getContainer();
|
||||
if (container != null && StringHelper.IndexOfIgnoreCase(container, "mp4") != -1) {
|
||||
profile.setContainer(container + ",m4v");
|
||||
if (container != null && container.toLowerCase().contains(ContainerTypes.MP4)) {
|
||||
profile.setContainer(container + "," + ContainerTypes.M4V);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ResponseProfile> responseProfiles = new ArrayList<>();
|
||||
responseProfiles.addAll(Arrays.asList(getResponseProfiles()));
|
||||
ArrayList<ResponseProfile> responseProfiles = new ArrayList<>(
|
||||
Arrays.asList(getResponseProfiles())
|
||||
);
|
||||
|
||||
ResponseProfile m4vProfile = new ResponseProfile();
|
||||
m4vProfile.setContainer("m4v");
|
||||
m4vProfile.setContainer(ContainerTypes.M4V);
|
||||
m4vProfile.setType(DlnaProfileType.Video);
|
||||
m4vProfile.setMimeType("video/mp4");
|
||||
responseProfiles.add(m4vProfile);
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.jellyfin.androidtv.model.compat;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecList;
|
||||
|
||||
import org.jellyfin.androidtv.constants.CodecTypes;
|
||||
import org.jellyfin.androidtv.constants.ContainerTypes;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecType;
|
||||
import org.jellyfin.apiclient.model.dlna.DeviceProfile;
|
||||
@@ -11,7 +14,6 @@ import org.jellyfin.apiclient.model.dlna.DlnaProfileType;
|
||||
import org.jellyfin.apiclient.model.dlna.ProfileCondition;
|
||||
import org.jellyfin.apiclient.model.dlna.ProfileConditionType;
|
||||
import org.jellyfin.apiclient.model.dlna.ProfileConditionValue;
|
||||
import org.jellyfin.apiclient.model.extensions.StringHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -80,9 +82,9 @@ public class Api16Builder {
|
||||
String[] videoProfiles = GetVideoProfiles(codecCapabilities);
|
||||
int maxLevel = getMaxLevel(codecCapabilities);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(profile.getCodec(), "h264")){
|
||||
if (CodecTypes.H264.equalsIgnoreCase(profile.getCodec())){
|
||||
if (videoProfiles.length > 0){
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, tangible.DotNetToJavaStringHelper.join("|", videoProfiles)));
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, Utils.join("|", videoProfiles)));
|
||||
}
|
||||
else{
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, Defaults.DefaultH264Profile));
|
||||
@@ -201,13 +203,13 @@ public class Api16Builder {
|
||||
|
||||
DirectPlayProfile profile = new DirectPlayProfile();
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(parts[0], "audio")) {
|
||||
if ("audio".equalsIgnoreCase(parts[0])) {
|
||||
profile.setType(DlnaProfileType.Audio);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(parts[0], "video")) {
|
||||
else if ("video".equalsIgnoreCase(parts[0])) {
|
||||
profile.setType(DlnaProfileType.Video);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(parts[0], "image")) {
|
||||
else if ("image".equalsIgnoreCase(parts[0])) {
|
||||
profile.setType(DlnaProfileType.Photo);
|
||||
}
|
||||
else{
|
||||
@@ -218,77 +220,73 @@ public class Api16Builder {
|
||||
|
||||
// Since we can't get supported codecs per container, we'll have to hardcode them
|
||||
if (profile.getType()==DlnaProfileType.Video){
|
||||
|
||||
profile.setContainer(codecType);
|
||||
|
||||
if (StringHelper.IndexOfIgnoreCase(codecType, "mp4") == 0){
|
||||
profile.setContainer("mp4,m4v");
|
||||
profile.setVideoCodec("h264,mpeg4");
|
||||
profile.setAudioCodec("aac");
|
||||
if (codecType.startsWith(ContainerTypes.MP4)){
|
||||
profile.setContainer(Utils.join(",", ContainerTypes.MP4, ContainerTypes.M4V));
|
||||
profile.setVideoCodec(Utils.join(",", CodecTypes.H264, CodecTypes.MPEG4));
|
||||
profile.setAudioCodec(CodecTypes.AAC);
|
||||
|
||||
if (Defaults.SupportsMkv){
|
||||
profile.setContainer(profile.getContainer() + ",mkv");
|
||||
profile.setContainer(Utils.join(",", profile.getContainer(), ContainerTypes.MKV));
|
||||
}
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("avc", codecType)){
|
||||
profile.setContainer("mp4,m4v");
|
||||
profile.setVideoCodec("h264,mpeg4");
|
||||
profile.setAudioCodec("aac");
|
||||
else if (CodecTypes.AVC.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(Utils.join(",", ContainerTypes.MP4, ContainerTypes.M4V));
|
||||
profile.setVideoCodec(Utils.join(",", CodecTypes.H264, CodecTypes.MPEG4));
|
||||
profile.setAudioCodec(CodecTypes.AAC);
|
||||
|
||||
if (Defaults.SupportsMkv){
|
||||
profile.setContainer(profile.getContainer() + ",mkv");
|
||||
profile.setContainer(Utils.join(",", profile.getContainer(), ContainerTypes.MKV));
|
||||
}
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("hevc", codecType)){
|
||||
profile.setContainer("mp4,m4v");
|
||||
profile.setVideoCodec("h265");
|
||||
profile.setAudioCodec("aac");
|
||||
else if (CodecTypes.HEVC.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(Utils.join(",", ContainerTypes.MP4, ContainerTypes.M4V));
|
||||
profile.setVideoCodec(CodecTypes.H265);
|
||||
profile.setAudioCodec(CodecTypes.AAC);
|
||||
|
||||
if (Defaults.SupportsMkv){
|
||||
profile.setContainer(profile.getContainer() + ",mkv");
|
||||
profile.setContainer(Utils.join(",", profile.getContainer(), ContainerTypes.MKV));
|
||||
}
|
||||
}
|
||||
else if (StringHelper.IndexOfIgnoreCase(codecType, "vp8") != -1){
|
||||
profile.setContainer("webm");
|
||||
else if (codecType.contains(CodecTypes.VP8)){
|
||||
profile.setContainer(ContainerTypes.WEBM);
|
||||
}
|
||||
else if (StringHelper.IndexOfIgnoreCase(codecType, "vp9") != -1){
|
||||
profile.setContainer("webm");
|
||||
else if (codecType.contains(CodecTypes.VP9)){
|
||||
profile.setContainer(ContainerTypes.WEBM);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("3gpp", codecType)){
|
||||
profile.setContainer("3gp");
|
||||
else if (ContainerTypes._3GPP.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(ContainerTypes._3GP);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("mpeg2", codecType)){
|
||||
profile.setContainer("ts,mpegts");
|
||||
profile.setVideoCodec("mpeg2video,mpeg2");
|
||||
profile.setAudioCodec("aac");
|
||||
else if (CodecTypes.MPEG2.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(Utils.join(",", ContainerTypes.TS, ContainerTypes.MPEGTS));
|
||||
profile.setVideoCodec(Utils.join(",", CodecTypes.MPEG2VIDEO, CodecTypes.MPEG2));
|
||||
profile.setAudioCodec(CodecTypes.AAC);
|
||||
|
||||
// If we need to add mp4, m4v or mkv then they must be kept separate from mpegts and in their own DirectPlayProfile
|
||||
// This is because they have different rules regarding ac3 within the container
|
||||
}
|
||||
else {
|
||||
|
||||
profile.setContainer(codecType);
|
||||
}
|
||||
}
|
||||
else if (profile.getType()==DlnaProfileType.Audio){
|
||||
|
||||
if (StringHelper.IndexOfIgnoreCase(codecType, "mp4") == 0){
|
||||
profile.setContainer("aac");
|
||||
if (codecType.startsWith(ContainerTypes.MP4)){
|
||||
profile.setContainer(CodecTypes.AAC);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("mpeg", codecType)){
|
||||
profile.setContainer("mp3");
|
||||
else if (ContainerTypes.MPEG.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(CodecTypes.MP3);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("3gpp", codecType)){
|
||||
profile.setContainer("3gp");
|
||||
else if (ContainerTypes._3GPP.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(ContainerTypes._3GP);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("vorbis", codecType)){
|
||||
profile.setContainer("webm,webma");
|
||||
else if (CodecTypes.VORBIS.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(Utils.join(",", ContainerTypes.WEBM, ContainerTypes.WEBMA));
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("opus", codecType)){
|
||||
profile.setContainer("oga,ogg");
|
||||
else if (CodecTypes.OPUS.equalsIgnoreCase(codecType)){
|
||||
profile.setContainer(Utils.join(",", ContainerTypes.OGA, ContainerTypes.OGG));
|
||||
}
|
||||
else {
|
||||
|
||||
// Will cover flac, gsm, and others
|
||||
profile.setContainer(codecType);
|
||||
}
|
||||
@@ -308,10 +306,10 @@ public class Api16Builder {
|
||||
CodecProfile profile = new CodecProfile();
|
||||
ArrayList<ProfileCondition> conditions = new ArrayList<ProfileCondition>();
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(parts[0], "audio")) {
|
||||
if ("audio".equalsIgnoreCase(parts[0])) {
|
||||
profile.setType(CodecType.Audio);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(parts[0], "video")) {
|
||||
else if ("video".equalsIgnoreCase(parts[0])) {
|
||||
profile.setType(CodecType.Video);
|
||||
}
|
||||
else{
|
||||
@@ -320,42 +318,40 @@ public class Api16Builder {
|
||||
|
||||
String codecType = parts[1].toLowerCase();
|
||||
|
||||
if (profile.getType()==CodecType.Video){
|
||||
|
||||
if (profile.getType()==CodecType.Video) {
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Width, "1920"));
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Height, "1080"));
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.NotEquals, ProfileConditionValue.IsAnamorphic, "true"));
|
||||
|
||||
if (StringHelper.IndexOfIgnoreCase(codecType, "avc") != -1){
|
||||
profile.setCodec("h264");
|
||||
if (codecType.contains(CodecTypes.AVC)) {
|
||||
profile.setCodec(CodecTypes.H264);
|
||||
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.VideoBitDepth, "8"));
|
||||
}
|
||||
else if (StringHelper.IndexOfIgnoreCase(codecType, "hevc") != -1){
|
||||
profile.setCodec("h265");
|
||||
else if (codecType.contains(CodecTypes.HEVC)) {
|
||||
profile.setCodec(CodecTypes.H265);
|
||||
}
|
||||
else if (StringHelper.IndexOfIgnoreCase(codecType, "vp8") != -1) {
|
||||
profile.setCodec("vp8");
|
||||
else if (codecType.contains(CodecTypes.VP8)) {
|
||||
profile.setCodec(CodecTypes.VP8);
|
||||
}
|
||||
else if (StringHelper.IndexOfIgnoreCase(codecType, "vp9") != -1) {
|
||||
profile.setCodec("vp9");
|
||||
else if (codecType.contains(CodecTypes.VP9)) {
|
||||
profile.setCodec(CodecTypes.VP9);
|
||||
}
|
||||
else{
|
||||
profile.setCodec(codecType);
|
||||
}
|
||||
}
|
||||
else if (profile.getType()==CodecType.Audio){
|
||||
|
||||
if (StringHelper.IndexOfIgnoreCase(codecType, "mp4") == 0){
|
||||
profile.setCodec("aac");
|
||||
if (codecType.startsWith(ContainerTypes.MP4)) {
|
||||
profile.setCodec(CodecTypes.AAC);
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("mpeg", codecType)){
|
||||
profile.setCodec("mp3");
|
||||
else if (ContainerTypes.MPEG.equalsIgnoreCase(codecType)) {
|
||||
profile.setCodec(CodecTypes.MP3);
|
||||
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioBitrate, "320000"));
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("opus", codecType)){
|
||||
profile.setCodec("vorbis");
|
||||
else if (CodecTypes.OPUS.equalsIgnoreCase(codecType)) {
|
||||
profile.setCodec(CodecTypes.VORBIS);
|
||||
}
|
||||
else{
|
||||
profile.setCodec(codecType);
|
||||
@@ -368,18 +364,15 @@ public class Api16Builder {
|
||||
|
||||
newlyAddedCodecProfiles.add(profile);
|
||||
|
||||
if (profile.getType()==CodecType.Audio && StringHelper.EqualsIgnoreCase("aac", profile.getCodec())) {
|
||||
if (profile.getType()==CodecType.Audio && CodecTypes.AAC.equalsIgnoreCase(profile.getCodec())) {
|
||||
// Create a duplicate under VideoAudioType
|
||||
CodecProfile videoAudioProfile = new CodecProfile();
|
||||
videoAudioProfile.setType(CodecType.VideoAudio);
|
||||
videoAudioProfile.setCodec("aac");
|
||||
videoAudioProfile.setCodec(CodecTypes.AAC);
|
||||
|
||||
ArrayList<ProfileCondition> videoAudioProfileConditions = new ArrayList<ProfileCondition>();
|
||||
|
||||
for (ProfileCondition pc : profile.getConditions()){
|
||||
|
||||
videoAudioProfileConditions.add(pc);
|
||||
}
|
||||
ArrayList<ProfileCondition> videoAudioProfileConditions = new ArrayList<>(
|
||||
Arrays.asList(profile.getConditions())
|
||||
);
|
||||
|
||||
videoAudioProfile.setConditions(videoAudioProfileConditions.toArray(new ProfileCondition[videoAudioProfileConditions.size()]));
|
||||
|
||||
@@ -392,19 +385,17 @@ public class Api16Builder {
|
||||
private boolean containsDirectPlayProfile(List<DirectPlayProfile> profiles, DirectPlayProfile newProfile){
|
||||
for (DirectPlayProfile profile : profiles){
|
||||
if (profile.getType() == newProfile.getType()) {
|
||||
if (StringHelper.EqualsIgnoreCase(profile.getContainer(), newProfile.getContainer())) {
|
||||
|
||||
if (Utils.equalsIgnoreCase(profile.getContainer(), newProfile.getContainer())) {
|
||||
if (profile.getType() == DlnaProfileType.Audio){
|
||||
if (StringHelper.EqualsIgnoreCase(profile.getAudioCodec(), newProfile.getAudioCodec())) {
|
||||
if (Utils.equalsIgnoreCase(profile.getAudioCodec(), newProfile.getAudioCodec())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (profile.getType() == DlnaProfileType.Video){
|
||||
if (StringHelper.EqualsIgnoreCase(profile.getVideoCodec(), newProfile.getVideoCodec())) {
|
||||
if (Utils.equalsIgnoreCase(profile.getVideoCodec(), newProfile.getVideoCodec())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,8 +405,7 @@ public class Api16Builder {
|
||||
private boolean containsCodecProfile(List<CodecProfile> profiles, CodecProfile newProfile){
|
||||
for (CodecProfile profile : profiles){
|
||||
if (profile.getType() == newProfile.getType()) {
|
||||
if (StringHelper.EqualsIgnoreCase(profile.getCodec(), newProfile.getCodec())) {
|
||||
|
||||
if (Utils.equalsIgnoreCase(profile.getCodec(), newProfile.getCodec())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ package org.jellyfin.androidtv.model.compat;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.util.Range;
|
||||
|
||||
import org.jellyfin.androidtv.constants.CodecTypes;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecType;
|
||||
import org.jellyfin.apiclient.model.dlna.ProfileCondition;
|
||||
import org.jellyfin.apiclient.model.dlna.ProfileConditionType;
|
||||
import org.jellyfin.apiclient.model.dlna.ProfileConditionValue;
|
||||
import org.jellyfin.apiclient.model.extensions.StringHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -38,8 +38,7 @@ public class Api21Builder extends Api16Builder {
|
||||
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.NotEquals, ProfileConditionValue.IsAnamorphic, "true"));
|
||||
|
||||
if (StringHelper.IndexOfIgnoreCase(profile.getCodec(), "h264") != -1) {
|
||||
|
||||
if (profile.getCodec() != null && profile.getCodec().toLowerCase().contains(CodecTypes.H264)) {
|
||||
conditions.add(new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.VideoBitDepth, "8"));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package org.jellyfin.androidtv.model.compat;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.jellyfin.androidtv.constants.CodecTypes;
|
||||
import org.jellyfin.androidtv.constants.MediaTypes;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.CodecType;
|
||||
import org.jellyfin.apiclient.model.dlna.ConditionProcessor;
|
||||
@@ -17,10 +20,7 @@ import org.jellyfin.apiclient.model.dlna.TranscodingProfile;
|
||||
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStream;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStreamType;
|
||||
import org.jellyfin.apiclient.model.extensions.FloatHelper;
|
||||
import org.jellyfin.apiclient.model.extensions.IntHelper;
|
||||
import org.jellyfin.apiclient.model.extensions.ListHelper;
|
||||
import org.jellyfin.apiclient.model.extensions.StringHelper;
|
||||
import org.jellyfin.apiclient.model.logging.ILogger;
|
||||
import org.jellyfin.apiclient.model.mediainfo.MediaProtocol;
|
||||
import org.jellyfin.apiclient.model.mediainfo.TransportStreamTimestamp;
|
||||
@@ -50,7 +50,7 @@ public class StreamBuilder
|
||||
java.util.ArrayList<MediaSourceInfo> mediaSources = new java.util.ArrayList<MediaSourceInfo>();
|
||||
for (MediaSourceInfo i : options.getMediaSources())
|
||||
{
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(options.getMediaSourceId()) || StringHelper.EqualsIgnoreCase(i.getId(), options.getMediaSourceId()))
|
||||
if (Utils.isEmpty(options.getMediaSourceId()) || Utils.equalsIgnoreCase(i.getId(), options.getMediaSourceId()))
|
||||
{
|
||||
mediaSources.add(i);
|
||||
}
|
||||
@@ -82,7 +82,7 @@ public class StreamBuilder
|
||||
java.util.ArrayList<MediaSourceInfo> mediaSources = new java.util.ArrayList<MediaSourceInfo>();
|
||||
for (MediaSourceInfo i : options.getMediaSources())
|
||||
{
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(options.getMediaSourceId()) || StringHelper.EqualsIgnoreCase(i.getId(), options.getMediaSourceId()))
|
||||
if (Utils.isEmpty(options.getMediaSourceId()) || Utils.equalsIgnoreCase(i.getId(), options.getMediaSourceId()))
|
||||
{
|
||||
mediaSources.add(i);
|
||||
}
|
||||
@@ -158,7 +158,7 @@ public class StreamBuilder
|
||||
String audioCodec = audioStream == null ? null : audioStream.getCodec();
|
||||
|
||||
// Make sure audio codec profiles are satisfied
|
||||
if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(audioCodec))
|
||||
if (!Utils.isEmpty(audioCodec))
|
||||
{
|
||||
java.util.ArrayList<ProfileCondition> conditions = new java.util.ArrayList<ProfileCondition>();
|
||||
for (CodecProfile i : options.getProfile().getCodecProfiles())
|
||||
@@ -237,7 +237,7 @@ public class StreamBuilder
|
||||
playlistItem.setEstimateContentLength(transcodingProfile.getEstimateContentLength());
|
||||
playlistItem.setContainer(transcodingProfile.getContainer());
|
||||
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(transcodingProfile.getAudioCodec()))
|
||||
if (Utils.isEmpty(transcodingProfile.getAudioCodec()))
|
||||
{
|
||||
playlistItem.setAudioCodecs(new String[] { });
|
||||
}
|
||||
@@ -391,7 +391,7 @@ public class StreamBuilder
|
||||
{
|
||||
for (SubtitleProfile profile : subtitleProfiles)
|
||||
{
|
||||
if (profile.getMethod() == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.getFormat(), stream.getCodec()))
|
||||
if (profile.getMethod() == SubtitleDeliveryMethod.External && Utils.equalsIgnoreCase(profile.getFormat(), stream.getCodec()))
|
||||
{
|
||||
return stream.getIndex();
|
||||
}
|
||||
@@ -495,16 +495,12 @@ public class StreamBuilder
|
||||
playlistItem.setCopyTimestamps(transcodingProfile.getCopyTimestamps());
|
||||
playlistItem.setEnableSubtitlesInManifest(transcodingProfile.getEnableSubtitlesInManifest());
|
||||
|
||||
if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(transcodingProfile.getMaxAudioChannels()))
|
||||
if (!Utils.isEmpty(transcodingProfile.getMaxAudioChannels()))
|
||||
{
|
||||
int transcodingMaxAudioChannels = 0;
|
||||
tangible.RefObject<Integer> tempRef_transcodingMaxAudioChannels = new tangible.RefObject<Integer>(transcodingMaxAudioChannels);
|
||||
boolean tempVar6 = IntHelper.TryParseCultureInvariant(transcodingProfile.getMaxAudioChannels(), tempRef_transcodingMaxAudioChannels);
|
||||
transcodingMaxAudioChannels = tempRef_transcodingMaxAudioChannels.argValue;
|
||||
if (tempVar6)
|
||||
{
|
||||
try {
|
||||
int transcodingMaxAudioChannels = Integer.parseInt(transcodingProfile.getMaxAudioChannels());
|
||||
playlistItem.setTranscodingMaxAudioChannels(transcodingMaxAudioChannels);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
playlistItem.setSubProtocol(transcodingProfile.getProtocol());
|
||||
playlistItem.setAudioStreamIndex(audioStreamIndex);
|
||||
@@ -631,10 +627,10 @@ public class StreamBuilder
|
||||
// Reduce the bitrate if we're downmixing
|
||||
if (targetAudioChannels != null && audioStream != null && audioStream.getChannels() != null && targetAudioChannels < audioStream.getChannels())
|
||||
{
|
||||
defaultBitrate = StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3") ? 192000 : 128000;
|
||||
defaultBitrate = CodecTypes.AC3.equalsIgnoreCase(targetAudioCodec) ? 192000 : 128000;
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(subProtocol, "hls"))
|
||||
if (MediaTypes.HLS.equalsIgnoreCase(subProtocol))
|
||||
{
|
||||
defaultBitrate = Math.min(384000, defaultBitrate);
|
||||
}
|
||||
@@ -764,7 +760,7 @@ public class StreamBuilder
|
||||
|
||||
String videoCodec = videoStream == null ? null : videoStream.getCodec();
|
||||
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(videoCodec))
|
||||
if (Utils.isEmpty(videoCodec))
|
||||
{
|
||||
String tempVar6 = profile.getName();
|
||||
String tempVar7 = mediaSource.getPath();
|
||||
@@ -813,7 +809,7 @@ public class StreamBuilder
|
||||
{
|
||||
String audioCodec = audioStream.getCodec();
|
||||
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(audioCodec))
|
||||
if (Utils.isEmpty(audioCodec))
|
||||
{
|
||||
String tempVar8 = profile.getName();
|
||||
String tempVar9 = mediaSource.getPath();
|
||||
@@ -910,7 +906,7 @@ public class StreamBuilder
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subtitleStream.getIsTextSubtitleStream() == MediaStream.IsTextFormat(profile.getFormat()) && StringHelper.EqualsIgnoreCase(profile.getFormat(), subtitleStream.getCodec()))
|
||||
if (subtitleStream.getIsTextSubtitleStream() == MediaStream.IsTextFormat(profile.getFormat()) && Utils.equalsIgnoreCase(profile.getFormat(), subtitleStream.getCodec()))
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
@@ -947,7 +943,7 @@ public class StreamBuilder
|
||||
|
||||
if ((profile.getMethod() == SubtitleDeliveryMethod.External && subtitleStream.getIsTextSubtitleStream() == MediaStream.IsTextFormat(profile.getFormat())) || (profile.getMethod() == SubtitleDeliveryMethod.Hls && subtitleStream.getIsTextSubtitleStream()))
|
||||
{
|
||||
boolean requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.getCodec(), profile.getFormat());
|
||||
boolean requiresConversion = !Utils.equalsIgnoreCase(subtitleStream.getCodec(), profile.getFormat());
|
||||
|
||||
if (!requiresConversion)
|
||||
{
|
||||
@@ -996,12 +992,12 @@ public class StreamBuilder
|
||||
{
|
||||
ValidateAudioInput(options);
|
||||
|
||||
if (options.getAudioStreamIndex() != null && tangible.DotNetToJavaStringHelper.isNullOrEmpty(options.getMediaSourceId()))
|
||||
if (options.getAudioStreamIndex() != null && Utils.isEmpty(options.getMediaSourceId()))
|
||||
{
|
||||
throw new IllegalArgumentException("MediaSourceId is required when a specific audio stream is requested");
|
||||
}
|
||||
|
||||
if (options.getSubtitleStreamIndex() != null && tangible.DotNetToJavaStringHelper.isNullOrEmpty(options.getMediaSourceId()))
|
||||
if (options.getSubtitleStreamIndex() != null && Utils.isEmpty(options.getMediaSourceId()))
|
||||
{
|
||||
throw new IllegalArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
|
||||
}
|
||||
@@ -1009,11 +1005,11 @@ public class StreamBuilder
|
||||
|
||||
private void ValidateAudioInput(AudioOptions options)
|
||||
{
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(options.getItemId()))
|
||||
if (Utils.isEmpty(options.getItemId()))
|
||||
{
|
||||
throw new IllegalArgumentException("ItemId is required");
|
||||
}
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(options.getDeviceId()))
|
||||
if (Utils.isEmpty(options.getDeviceId()))
|
||||
{
|
||||
throw new IllegalArgumentException("DeviceId is required");
|
||||
}
|
||||
@@ -1033,7 +1029,7 @@ public class StreamBuilder
|
||||
{
|
||||
String value = condition.getValue();
|
||||
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(value))
|
||||
if (Utils.isEmpty(value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1048,26 +1044,18 @@ public class StreamBuilder
|
||||
{
|
||||
case AudioBitrate:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar = IntHelper.TryParseCultureInvariant(value, tempRef_num);
|
||||
num = tempRef_num.argValue;
|
||||
if (tempVar)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setAudioBitrate(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case AudioChannels:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num2 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar2 = IntHelper.TryParseCultureInvariant(value, tempRef_num2);
|
||||
num = tempRef_num2.argValue;
|
||||
if (tempVar2)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setMaxAudioChannels(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case IsAnamorphic:
|
||||
@@ -1084,26 +1072,18 @@ public class StreamBuilder
|
||||
}
|
||||
case RefFrames:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num3 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar3 = IntHelper.TryParseCultureInvariant(value, tempRef_num3);
|
||||
num = tempRef_num3.argValue;
|
||||
if (tempVar3)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setMaxRefFrames(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case VideoBitDepth:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num4 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar4 = IntHelper.TryParseCultureInvariant(value, tempRef_num4);
|
||||
num = tempRef_num4.argValue;
|
||||
if (tempVar4)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setMaxVideoBitDepth(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case VideoProfile:
|
||||
@@ -1113,62 +1093,42 @@ public class StreamBuilder
|
||||
}
|
||||
case Height:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num5 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar5 = IntHelper.TryParseCultureInvariant(value, tempRef_num5);
|
||||
num = tempRef_num5.argValue;
|
||||
if (tempVar5)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setMaxHeight(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case VideoBitrate:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num6 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar6 = IntHelper.TryParseCultureInvariant(value, tempRef_num6);
|
||||
num = tempRef_num6.argValue;
|
||||
if (tempVar6)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setVideoBitrate(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case VideoFramerate:
|
||||
{
|
||||
float num = 0F;
|
||||
tangible.RefObject<Float> tempRef_num7 = new tangible.RefObject<Float>(num);
|
||||
boolean tempVar7 = FloatHelper.TryParseCultureInvariant(value, tempRef_num7);
|
||||
num = tempRef_num7.argValue;
|
||||
if (tempVar7)
|
||||
{
|
||||
try {
|
||||
float num = Float.parseFloat(value);
|
||||
item.setMaxFramerate(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case VideoLevel:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num8 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar8 = IntHelper.TryParseCultureInvariant(value, tempRef_num8);
|
||||
num = tempRef_num8.argValue;
|
||||
if (tempVar8)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setVideoLevel(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
case Width:
|
||||
{
|
||||
int num = 0;
|
||||
tangible.RefObject<Integer> tempRef_num9 = new tangible.RefObject<Integer>(num);
|
||||
boolean tempVar9 = IntHelper.TryParseCultureInvariant(value, tempRef_num9);
|
||||
num = tempRef_num9.argValue;
|
||||
if (tempVar9)
|
||||
{
|
||||
try {
|
||||
int num = Integer.parseInt(value);
|
||||
item.setMaxWidth(num);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1185,7 +1145,7 @@ public class StreamBuilder
|
||||
boolean any = false;
|
||||
for (String i : profile.GetContainers())
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(i, mediaContainer))
|
||||
if (Utils.equalsIgnoreCase(i, mediaContainer))
|
||||
{
|
||||
any = true;
|
||||
break;
|
||||
@@ -1203,7 +1163,7 @@ public class StreamBuilder
|
||||
{
|
||||
// Check audio codecs
|
||||
String audioCodec = audioStream == null ? null : audioStream.getCodec();
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
|
||||
if (Utils.isEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1222,7 +1182,7 @@ public class StreamBuilder
|
||||
boolean any = false;
|
||||
for (String i : profile.GetContainers())
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(i, mediaContainer))
|
||||
if (Utils.equalsIgnoreCase(i, mediaContainer))
|
||||
{
|
||||
any = true;
|
||||
break;
|
||||
@@ -1239,7 +1199,7 @@ public class StreamBuilder
|
||||
if (videoCodecs.size() > 0)
|
||||
{
|
||||
String videoCodec = videoStream == null ? null : videoStream.getCodec();
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(videoCodec) || !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
|
||||
if (Utils.isEmpty(videoCodec) || !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1251,7 +1211,7 @@ public class StreamBuilder
|
||||
{
|
||||
// Check audio codecs
|
||||
String audioCodec = audioStream == null ? null : audioStream.getCodec();
|
||||
if (tangible.DotNetToJavaStringHelper.isNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
|
||||
if (Utils.isEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.jellyfin.androidtv.model.compat;
|
||||
|
||||
import org.jellyfin.androidtv.constants.ContainerTypes;
|
||||
import org.jellyfin.androidtv.constants.MediaTypes;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -14,7 +16,6 @@ import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
|
||||
import org.jellyfin.apiclient.model.dto.NameValuePair;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStream;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStreamType;
|
||||
import org.jellyfin.apiclient.model.extensions.StringHelper;
|
||||
import org.jellyfin.apiclient.model.mediainfo.MediaProtocol;
|
||||
import org.jellyfin.apiclient.model.mediainfo.TransportStreamTimestamp;
|
||||
import org.jellyfin.apiclient.model.session.PlayMethod;
|
||||
@@ -26,6 +27,10 @@ import org.jellyfin.apiclient.model.session.PlayMethod;
|
||||
*/
|
||||
@Deprecated
|
||||
public class StreamInfo {
|
||||
private static final String START_TIME_TICKS = "StartTimeTicks";
|
||||
private static final String SUBTITLE_STREAM_INDEX = "SubtitleStreamIndex";
|
||||
private static final String STATIC = "Static";
|
||||
|
||||
public StreamInfo() {
|
||||
setAudioCodecs(new String[]{});
|
||||
}
|
||||
@@ -428,13 +433,13 @@ public class StreamInfo {
|
||||
}
|
||||
|
||||
// Try to keep the url clean by omitting defaults
|
||||
if (StringHelper.EqualsIgnoreCase(pair.getName(), "StartTimeTicks") && StringHelper.EqualsIgnoreCase(pair.getValue(), "0")) {
|
||||
if (START_TIME_TICKS.equalsIgnoreCase(pair.getName()) && "0".equalsIgnoreCase(pair.getValue())) {
|
||||
continue;
|
||||
}
|
||||
if (StringHelper.EqualsIgnoreCase(pair.getName(), "SubtitleStreamIndex") && StringHelper.EqualsIgnoreCase(pair.getValue(), "-1")) {
|
||||
if (SUBTITLE_STREAM_INDEX.equalsIgnoreCase(pair.getName()) && "-1".equalsIgnoreCase(pair.getValue())) {
|
||||
continue;
|
||||
}
|
||||
if (StringHelper.EqualsIgnoreCase(pair.getName(), "Static") && StringHelper.EqualsIgnoreCase(pair.getValue(), "false")) {
|
||||
if (STATIC.equalsIgnoreCase(pair.getName()) && "false".equalsIgnoreCase(pair.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -466,14 +471,14 @@ public class StreamInfo {
|
||||
baseUrl = baseUrl.replaceAll("[/]+$", "");
|
||||
|
||||
if (getMediaType() == DlnaProfileType.Audio) {
|
||||
if (StringHelper.EqualsIgnoreCase(getSubProtocol(), "hls")) {
|
||||
if (MediaTypes.HLS.equalsIgnoreCase(getSubProtocol())) {
|
||||
return String.format("%1$s/audio/%2$s/master.m3u8?%3$s", baseUrl, getItemId(), queryString);
|
||||
}
|
||||
|
||||
return String.format("%1$s/audio/%2$s/stream%3$s?%4$s", baseUrl, getItemId(), extension, queryString);
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(getSubProtocol(), "hls")) {
|
||||
if (MediaTypes.HLS.equalsIgnoreCase(getSubProtocol())) {
|
||||
return String.format("%1$s/videos/%2$s/master.m3u8?%3$s", baseUrl, getItemId(), queryString);
|
||||
}
|
||||
|
||||
@@ -501,29 +506,29 @@ public class StreamInfo {
|
||||
list.add(new NameValuePair("DeviceId", (tempVar2 != null) ? tempVar2 : ""));
|
||||
String tempVar3 = item.getMediaSourceId();
|
||||
list.add(new NameValuePair("MediaSourceId", (tempVar3 != null) ? tempVar3 : ""));
|
||||
list.add(new NameValuePair("Static", (new Boolean(item.getIsDirectStream())).toString().toLowerCase()));
|
||||
list.add(new NameValuePair(STATIC, Boolean.valueOf(item.getIsDirectStream()).toString().toLowerCase()));
|
||||
String tempVar4 = item.getVideoCodec();
|
||||
list.add(new NameValuePair("VideoCodec", (tempVar4 != null) ? tempVar4 : ""));
|
||||
list.add(new NameValuePair("AudioCodec", audioCodecs));
|
||||
list.add(new NameValuePair("AudioStreamIndex", item.getAudioStreamIndex() != null ? StringHelper.ToStringCultureInvariant(item.getAudioStreamIndex()) : ""));
|
||||
list.add(new NameValuePair("SubtitleStreamIndex", item.getSubtitleStreamIndex() != null && item.getSubtitleDeliveryMethod() != org.jellyfin.apiclient.model.dlna.SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.getSubtitleStreamIndex()) : ""));
|
||||
list.add(new NameValuePair("VideoBitrate", item.getVideoBitrate() != null ? StringHelper.ToStringCultureInvariant(item.getVideoBitrate()) : ""));
|
||||
list.add(new NameValuePair("AudioBitrate", item.getAudioBitrate() != null ? StringHelper.ToStringCultureInvariant(item.getAudioBitrate()) : ""));
|
||||
list.add(new NameValuePair("MaxAudioChannels", item.getMaxAudioChannels() != null ? StringHelper.ToStringCultureInvariant(item.getMaxAudioChannels()) : ""));
|
||||
list.add(new NameValuePair("AudioStreamIndex", item.getAudioStreamIndex() != null ? String.valueOf(item.getAudioStreamIndex()) : ""));
|
||||
list.add(new NameValuePair("SubtitleStreamIndex", item.getSubtitleStreamIndex() != null && item.getSubtitleDeliveryMethod() != org.jellyfin.apiclient.model.dlna.SubtitleDeliveryMethod.External ? String.valueOf(item.getSubtitleStreamIndex()) : ""));
|
||||
list.add(new NameValuePair("VideoBitrate", item.getVideoBitrate() != null ? String.valueOf(item.getVideoBitrate()) : ""));
|
||||
list.add(new NameValuePair("AudioBitrate", item.getAudioBitrate() != null ? String.valueOf(item.getAudioBitrate()) : ""));
|
||||
list.add(new NameValuePair("MaxAudioChannels", item.getMaxAudioChannels() != null ? String.valueOf(item.getMaxAudioChannels()) : ""));
|
||||
list.add(new NameValuePair("MaxFramerate", item.getMaxFramerate() != null ? String.valueOf(item.getMaxFramerate()) : ""));
|
||||
list.add(new NameValuePair("MaxWidth", item.getMaxWidth() != null ? StringHelper.ToStringCultureInvariant(item.getMaxWidth()) : ""));
|
||||
list.add(new NameValuePair("MaxHeight", item.getMaxHeight() != null ? StringHelper.ToStringCultureInvariant(item.getMaxHeight()) : ""));
|
||||
list.add(new NameValuePair("MaxWidth", item.getMaxWidth() != null ? String.valueOf(item.getMaxWidth()) : ""));
|
||||
list.add(new NameValuePair("MaxHeight", item.getMaxHeight() != null ? String.valueOf(item.getMaxHeight()) : ""));
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(item.getSubProtocol(), "hls") && !item.getForceLiveStream()) {
|
||||
list.add(new NameValuePair("StartTimeTicks", ""));
|
||||
if (MediaTypes.HLS.equalsIgnoreCase(item.getSubProtocol()) && !item.getForceLiveStream()) {
|
||||
list.add(new NameValuePair(START_TIME_TICKS, ""));
|
||||
} else {
|
||||
list.add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(item.getStartPositionTicks())));
|
||||
list.add(new NameValuePair(START_TIME_TICKS, String.valueOf(item.getStartPositionTicks())));
|
||||
}
|
||||
|
||||
list.add(new NameValuePair("Level", item.getVideoLevel() != null ? StringHelper.ToStringCultureInvariant(item.getVideoLevel()) : ""));
|
||||
list.add(new NameValuePair("Level", item.getVideoLevel() != null ? String.valueOf(item.getVideoLevel()) : ""));
|
||||
|
||||
list.add(new NameValuePair("MaxRefFrames", item.getMaxRefFrames() != null ? StringHelper.ToStringCultureInvariant(item.getMaxRefFrames()) : ""));
|
||||
list.add(new NameValuePair("MaxVideoBitDepth", item.getMaxVideoBitDepth() != null ? StringHelper.ToStringCultureInvariant(item.getMaxVideoBitDepth()) : ""));
|
||||
list.add(new NameValuePair("MaxRefFrames", item.getMaxRefFrames() != null ? String.valueOf(item.getMaxRefFrames()) : ""));
|
||||
list.add(new NameValuePair("MaxVideoBitDepth", item.getMaxVideoBitDepth() != null ? String.valueOf(item.getMaxVideoBitDepth()) : ""));
|
||||
String tempVar5 = item.getVideoProfile();
|
||||
list.add(new NameValuePair("Profile", (tempVar5 != null) ? tempVar5 : ""));
|
||||
|
||||
@@ -541,12 +546,12 @@ public class StreamInfo {
|
||||
list.add(new NameValuePair("ItemId", item.getItemId()));
|
||||
}
|
||||
|
||||
list.add(new NameValuePair("CopyTimestamps", (new Boolean(item.getCopyTimestamps())).toString().toLowerCase()));
|
||||
list.add(new NameValuePair("ForceLiveStream", (new Boolean(item.getForceLiveStream())).toString().toLowerCase()));
|
||||
list.add(new NameValuePair("CopyTimestamps", Boolean.valueOf(item.getCopyTimestamps()).toString().toLowerCase()));
|
||||
list.add(new NameValuePair("ForceLiveStream", Boolean.valueOf(item.getForceLiveStream()).toString().toLowerCase()));
|
||||
list.add(new NameValuePair("SubtitleMethod", item.getSubtitleStreamIndex() != null && item.getSubtitleDeliveryMethod() != org.jellyfin.apiclient.model.dlna.SubtitleDeliveryMethod.External ? item.getSubtitleDeliveryMethod().toString() : ""));
|
||||
|
||||
list.add(new NameValuePair("TranscodingMaxAudioChannels", item.getTranscodingMaxAudioChannels() != null ? StringHelper.ToStringCultureInvariant(item.getTranscodingMaxAudioChannels()) : ""));
|
||||
list.add(new NameValuePair("EnableSubtitlesInManifest", (new Boolean(item.getEnableSubtitlesInManifest())).toString().toLowerCase()));
|
||||
list.add(new NameValuePair("TranscodingMaxAudioChannels", item.getTranscodingMaxAudioChannels() != null ? String.valueOf(item.getTranscodingMaxAudioChannels()) : ""));
|
||||
list.add(new NameValuePair("EnableSubtitlesInManifest", Boolean.valueOf(item.getEnableSubtitlesInManifest()).toString().toLowerCase()));
|
||||
|
||||
String tempVar7 = item.getMediaSource().getETag();
|
||||
list.add(new NameValuePair("Tag", (tempVar7 != null) ? tempVar7 : ""));
|
||||
@@ -580,7 +585,7 @@ public class StreamInfo {
|
||||
ArrayList<SubtitleStreamInfo> list = new ArrayList<SubtitleStreamInfo>();
|
||||
|
||||
// HLS will preserve timestamps so we can just grab the full subtitle stream
|
||||
long startPositionTicks = StringHelper.EqualsIgnoreCase(getSubProtocol(), "hls") ? 0 : (getPlayMethod() == PlayMethod.Transcode && !getCopyTimestamps() ? getStartPositionTicks() : 0);
|
||||
long startPositionTicks = MediaTypes.HLS.equalsIgnoreCase(getSubProtocol()) ? 0 : (getPlayMethod() == PlayMethod.Transcode && !getCopyTimestamps() ? getStartPositionTicks() : 0);
|
||||
|
||||
// First add the selected track
|
||||
if (getSubtitleStreamIndex() != null) {
|
||||
@@ -630,8 +635,8 @@ public class StreamInfo {
|
||||
SubtitleStreamInfo info = tempVar;
|
||||
|
||||
if (info.getDeliveryMethod() == SubtitleDeliveryMethod.External) {
|
||||
if (getMediaSource().getProtocol() == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.getCodec(), subtitleProfile.getFormat())) {
|
||||
info.setUrl(String.format("%1$s/Videos/%2$s/%3$s/Subtitles/%4$s/%5$s/Stream.%6$s", baseUrl, getItemId(), getMediaSourceId(), StringHelper.ToStringCultureInvariant(stream.getIndex()), StringHelper.ToStringCultureInvariant(startPositionTicks), subtitleProfile.getFormat()));
|
||||
if (getMediaSource().getProtocol() == MediaProtocol.File || !stream.getCodec().equalsIgnoreCase(subtitleProfile.getFormat())) {
|
||||
info.setUrl(String.format("%1$s/Videos/%2$s/%3$s/Subtitles/%4$s/%5$s/Stream.%6$s", baseUrl, getItemId(), getMediaSourceId(), String.valueOf(stream.getIndex()), String.valueOf(startPositionTicks), subtitleProfile.getFormat()));
|
||||
|
||||
if (!Utils.isEmpty(accessToken)) {
|
||||
info.setUrl(info.getUrl() + "?api_key=" + accessToken);
|
||||
@@ -777,7 +782,7 @@ public class StreamInfo {
|
||||
}
|
||||
|
||||
for (String codec : getAudioCodecs()) {
|
||||
if (StringHelper.EqualsIgnoreCase(codec, inputCodec)) {
|
||||
if (codec.equalsIgnoreCase(inputCodec)) {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
@@ -815,8 +820,7 @@ public class StreamInfo {
|
||||
}
|
||||
|
||||
public final TransportStreamTimestamp getTargetTimestamp() {
|
||||
TransportStreamTimestamp defaultValue = StringHelper.EqualsIgnoreCase(getContainer(), "m2ts") ? TransportStreamTimestamp.Valid : TransportStreamTimestamp.None;
|
||||
|
||||
TransportStreamTimestamp defaultValue = ContainerTypes.M2TS.equalsIgnoreCase(getContainer()) ? TransportStreamTimestamp.Valid : TransportStreamTimestamp.None;
|
||||
TransportStreamTimestamp tempVar = getMediaSource().getTimestamp();
|
||||
return !getIsDirectStream() ? defaultValue : getMediaSource() == null ? defaultValue : (tempVar != null) ? tempVar : TransportStreamTimestamp.None;
|
||||
}
|
||||
|
||||
@@ -9,4 +9,5 @@ public class AudioEventListener {
|
||||
public void onPlaybackStateChange(PlaybackController.PlaybackState newState, BaseItemDto currentItem) {}
|
||||
public void onProgress(long pos) {}
|
||||
public void onQueueStatusChanged(boolean hasQueue) {}
|
||||
public void onQueueReplaced(){}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,16 @@ import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
@@ -83,13 +83,12 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
private TextView mSongTitle;
|
||||
private TextView mAlbumTitle;
|
||||
private TextView mCurrentNdx;
|
||||
private TextView mTotal;
|
||||
private ImageView mPoster;
|
||||
private ProgressBar mCurrentProgress;
|
||||
private TextView mCurrentPos;
|
||||
private TextView mRemainingTime;
|
||||
private int mCurrentDuration;
|
||||
private RowsFragment mRowsFragment;
|
||||
private RowsSupportFragment mRowsFragment;
|
||||
private ArrayObjectAdapter mRowsAdapter;
|
||||
private static PositionableListRowPresenter mAudioQueuePresenter;
|
||||
|
||||
@@ -119,36 +118,37 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
mActivity = this;
|
||||
roboto = mApplication.getDefaultFont();
|
||||
|
||||
mClock = (ClockUserView) findViewById(R.id.clock);
|
||||
mPoster = (ImageView) findViewById(R.id.poster);
|
||||
mArtistName = (TextView) findViewById(R.id.artistTitle);
|
||||
mClock = findViewById(R.id.clock);
|
||||
mPoster = findViewById(R.id.poster);
|
||||
mArtistName = findViewById(R.id.artistTitle);
|
||||
mArtistName.setTypeface(roboto);
|
||||
mGenreRow = (LinearLayout) findViewById(R.id.genreRow);
|
||||
mSongTitle = (TextView) findViewById(R.id.songTitle);
|
||||
mGenreRow = findViewById(R.id.genreRow);
|
||||
mSongTitle = findViewById(R.id.song);
|
||||
mSongTitle.setTypeface(roboto);
|
||||
mAlbumTitle = (TextView) findViewById(R.id.albumTitle);
|
||||
mAlbumTitle = findViewById(R.id.album);
|
||||
mAlbumTitle.setTypeface(roboto);
|
||||
mCurrentNdx = (TextView) findViewById(R.id.currentNdx);
|
||||
mScrollView = (ScrollView) findViewById(R.id.mainScroller);
|
||||
mCounter = (TextView) findViewById(R.id.counter);
|
||||
mCurrentNdx = findViewById(R.id.track);
|
||||
mCurrentNdx.setTypeface(roboto);
|
||||
mScrollView = findViewById(R.id.mainScroller);
|
||||
mCounter = findViewById(R.id.counter);
|
||||
mCounter.setTypeface(roboto);
|
||||
mLogoImage = (ImageView) findViewById(R.id.artistLogo);
|
||||
mLogoImage = findViewById(R.id.artistLogo);
|
||||
|
||||
mSSArea = (RelativeLayout) findViewById(R.id.ssInfoArea);
|
||||
mSSTime = (TextView) findViewById(R.id.ssTime);
|
||||
mSSArea = findViewById(R.id.ssInfoArea);
|
||||
mSSTime = findViewById(R.id.ssTime);
|
||||
mSSTime.setTypeface(roboto);
|
||||
mSSAlbumSong = (TextView) findViewById(R.id.ssAlbumSong);
|
||||
mSSAlbumSong = findViewById(R.id.ssAlbumSong);
|
||||
mSSAlbumSong.setTypeface(roboto);
|
||||
mSSQueueStatus = (TextView) findViewById(R.id.ssQueueStatus);
|
||||
mSSQueueStatus = findViewById(R.id.ssQueueStatus);
|
||||
mSSQueueStatus.setTypeface(roboto);
|
||||
mSSUpNext = (TextView) findViewById(R.id.ssUpNext);
|
||||
mSSUpNext = findViewById(R.id.ssUpNext);
|
||||
mSSUpNext.setTypeface(roboto);
|
||||
|
||||
mPlayPauseButton = (ImageButton) findViewById(R.id.playPauseBtn);
|
||||
mPlayPauseButton.setSecondaryImage(R.drawable.lb_ic_pause);
|
||||
mPlayPauseButton.setPrimaryImage(R.drawable.play);
|
||||
TextView helpView = (TextView) findViewById(R.id.buttonTip);
|
||||
mPrevButton = (ImageButton) findViewById(R.id.prevBtn);
|
||||
mPlayPauseButton = findViewById(R.id.playPauseBtn);
|
||||
mPlayPauseButton.setSecondaryImage(R.drawable.ic_pause);
|
||||
mPlayPauseButton.setPrimaryImage(R.drawable.ic_play);
|
||||
TextView helpView = findViewById(R.id.buttonTip);
|
||||
mPrevButton = findViewById(R.id.prevBtn);
|
||||
mPrevButton.setHelpView(helpView);
|
||||
mPrevButton.setHelpText(getString(R.string.lbl_prev_item));
|
||||
mPrevButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -158,7 +158,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
mPrevButton.setGotFocusListener(mainAreaFocusListener);
|
||||
mNextButton = (ImageButton) findViewById(R.id.nextBtn);
|
||||
mNextButton = findViewById(R.id.nextBtn);
|
||||
mNextButton.setHelpView(helpView);
|
||||
mNextButton.setHelpText(getString(R.string.lbl_next_item));
|
||||
mNextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -168,11 +168,11 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
mNextButton.setGotFocusListener(mainAreaFocusListener);
|
||||
mRepeatButton = (ImageButton) findViewById(R.id.repeatBtn);
|
||||
mRepeatButton = findViewById(R.id.repeatBtn);
|
||||
mRepeatButton.setHelpView(helpView);
|
||||
mRepeatButton.setHelpText(getString(R.string.lbl_toggle_repeat));
|
||||
mRepeatButton.setPrimaryImage(R.drawable.loop);
|
||||
mRepeatButton.setSecondaryImage(R.drawable.loopred);
|
||||
mRepeatButton.setHelpText(getString(R.string.lbl_repeat));
|
||||
mRepeatButton.setPrimaryImage(R.drawable.ic_loop);
|
||||
mRepeatButton.setSecondaryImage(R.drawable.ic_loop_red);
|
||||
mRepeatButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -180,7 +180,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
updateButtons(MediaManager.isPlayingAudio());
|
||||
}
|
||||
});
|
||||
mSaveButton = (ImageButton) findViewById(R.id.saveBtn);
|
||||
mSaveButton = findViewById(R.id.saveBtn);
|
||||
mSaveButton.setHelpView(helpView);
|
||||
mSaveButton.setHelpText(getString(R.string.lbl_save_as_playlist));
|
||||
mSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -190,7 +190,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
mRepeatButton.setGotFocusListener(mainAreaFocusListener);
|
||||
mShuffleButton = (ImageButton) findViewById(R.id.shuffleBtn);
|
||||
mShuffleButton = findViewById(R.id.shuffleBtn);
|
||||
mShuffleButton.setHelpView(helpView);
|
||||
mShuffleButton.setHelpText(getString(R.string.lbl_reshuffle_queue));
|
||||
mShuffleButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -203,8 +203,6 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
MediaManager.shuffleAudioQueue();
|
||||
mRowsAdapter.remove(mQueueRow);
|
||||
addQueue();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(mActivity.getString(R.string.lbl_no), null)
|
||||
@@ -212,7 +210,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
mShuffleButton.setGotFocusListener(mainAreaFocusListener);
|
||||
mAlbumButton = (ImageButton) findViewById(R.id.albumBtn);
|
||||
mAlbumButton = findViewById(R.id.albumBtn);
|
||||
mAlbumButton.setHelpView(helpView);
|
||||
mAlbumButton.setHelpText(getString(R.string.lbl_open_album));
|
||||
mAlbumButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -224,7 +222,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
}
|
||||
});
|
||||
mAlbumButton.setGotFocusListener(mainAreaFocusListener);
|
||||
mArtistButton = (ImageButton) findViewById(R.id.artistBtn);
|
||||
mArtistButton = findViewById(R.id.artistBtn);
|
||||
mArtistButton.setHelpView(helpView);
|
||||
mArtistButton.setHelpText(getString(R.string.lbl_open_artist));
|
||||
mArtistButton.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -240,10 +238,9 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
});
|
||||
mArtistButton.setGotFocusListener(mainAreaFocusListener);
|
||||
|
||||
mCurrentProgress = (ProgressBar) findViewById(R.id.playerProgress);
|
||||
mCurrentPos = (TextView) findViewById(R.id.currentPos);
|
||||
mRemainingTime = (TextView) findViewById(R.id.remainingTime);
|
||||
mTotal = (TextView) findViewById(R.id.total);
|
||||
mCurrentProgress = findViewById(R.id.playerProgress);
|
||||
mCurrentPos = findViewById(R.id.currentPos);
|
||||
mRemainingTime = findViewById(R.id.remainingTime);
|
||||
|
||||
mPlayPauseButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@@ -255,13 +252,12 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
|
||||
BackgroundManager backgroundManager = BackgroundManager.getInstance(this);
|
||||
backgroundManager.attach(getWindow());
|
||||
backgroundManager.setDimLayer(getDrawable(R.drawable.left_fade));
|
||||
mBackgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
|
||||
mMetrics = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
|
||||
|
||||
mRowsFragment = new RowsFragment();
|
||||
getFragmentManager().beginTransaction().add(R.id.rowsFragment, mRowsFragment).commit();
|
||||
mRowsFragment = new RowsSupportFragment();
|
||||
getSupportFragmentManager().beginTransaction().add(R.id.rowsFragment, mRowsFragment).commit();
|
||||
|
||||
//create list background gradient
|
||||
mListBackground = mApplication.getCurrentBackgroundGradient();
|
||||
@@ -343,8 +339,6 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
MediaManager.prevAudioItem();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (ssActive) {
|
||||
@@ -385,6 +379,12 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
finish(); // entire queue removed nothing to do here
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueReplaced() {
|
||||
mRowsAdapter.remove(mQueueRow);
|
||||
addQueue();
|
||||
}
|
||||
};
|
||||
|
||||
private GotFocusEvent mainAreaFocusListener = new GotFocusEvent() {
|
||||
@@ -410,7 +410,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
Picasso.with(mActivity)
|
||||
.load(primaryImageUrl)
|
||||
.skipMemoryCache()
|
||||
.error(R.drawable.audioicon)
|
||||
.error(R.drawable.ic_album)
|
||||
.resize(posterWidth, posterHeight)
|
||||
.centerInside()
|
||||
.into(mPoster);
|
||||
@@ -460,16 +460,16 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
if (item != null) {
|
||||
mArtistName.setText(getArtistName(item));
|
||||
mSongTitle.setText(item.getName());
|
||||
mAlbumTitle.setText(item.getAlbum());
|
||||
mCurrentNdx.setText(MediaManager.getCurrentAudioQueueDisplayPosition());
|
||||
mTotal.setText(MediaManager.getCurrentAudioQueueDisplaySize());
|
||||
mAlbumTitle.setText(String.format(getResources().getString(R.string.lbl_now_playing_album), item.getAlbum()));
|
||||
mCurrentNdx.setText(String.format(getResources().getString(R.string.lbl_now_playing_track),
|
||||
MediaManager.getCurrentAudioQueueDisplayPosition(),
|
||||
MediaManager.getCurrentAudioQueueDisplaySize()));
|
||||
mCurrentDuration = ((Long)((item.getRunTimeTicks() != null ? item.getRunTimeTicks() : 0) / 10000)).intValue();
|
||||
//set progress to match duration
|
||||
mCurrentProgress.setMax(mCurrentDuration);
|
||||
addGenres(mGenreRow);
|
||||
updateBackground(ImageUtils.getBackdropImageUrl(item, TvApp.getApplication().getApiClient(), true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setCurrentTime(long time) {
|
||||
@@ -489,7 +489,7 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
for (String genre : mBaseItem.getGenres()) {
|
||||
if (!first) InfoLayoutHelper.addSpacer(this, layout, " / ", 14);
|
||||
first = false;
|
||||
layout.addView(new GenreButton(this, roboto, 16, genre, mBaseItem.getType()));
|
||||
layout.addView(new GenreButton(this, roboto, 16, genre, mBaseItem.getBaseItemType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -601,5 +601,4 @@ public class AudioNowPlayingActivity extends BaseActivity {
|
||||
.into(mBackgroundTarget);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.jellyfin.androidtv.playback;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -12,17 +11,11 @@ import android.media.AudioManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
@@ -50,7 +43,6 @@ import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.base.CustomMessage;
|
||||
import org.jellyfin.androidtv.base.IMessageListener;
|
||||
import org.jellyfin.androidtv.integration.RecommendationManager;
|
||||
import org.jellyfin.androidtv.itemhandling.BaseRowItem;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
import org.jellyfin.androidtv.livetv.ILiveTvGuide;
|
||||
@@ -80,14 +72,6 @@ import org.jellyfin.androidtv.util.TimeUtils;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.BaseItemUtils;
|
||||
import org.jellyfin.androidtv.util.apiclient.StreamHelper;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
@@ -100,6 +84,24 @@ import org.jellyfin.apiclient.model.livetv.SeriesTimerInfoDto;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackEvent;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackInfo;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
|
||||
public class CustomPlaybackOverlayFragment extends Fragment implements IPlaybackOverlayFragment, ILiveTvGuide {
|
||||
ImageView mPoster;
|
||||
ImageView mStudioImage;
|
||||
@@ -115,7 +117,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
LinearLayout mInfoRow;
|
||||
LinearLayout mButtonRow;
|
||||
FrameLayout mPopupArea;
|
||||
RowsFragment mPopupRowsFragment;
|
||||
RowsSupportFragment mPopupRowsFragment;
|
||||
ArrayObjectAdapter mPopupRowAdapter;
|
||||
ListRow mChapterRow;
|
||||
PositionableListRowPresenter mPopupRowPresenter;
|
||||
@@ -204,11 +206,10 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mApplication = TvApp.getApplication();
|
||||
mActivity = (PlaybackOverlayActivity) getActivity();
|
||||
|
||||
//stop any audio that may be playing
|
||||
// stop any audio that may be playing
|
||||
MediaManager.stopAudio();
|
||||
|
||||
mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
if (mAudioManager == null) {
|
||||
mApplication.getLogger().Error("Unable to get audio manager");
|
||||
Utils.showToast(getActivity(), R.string.msg_cannot_play_time);
|
||||
@@ -219,7 +220,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mActivity.setKeyListener(keyListener);
|
||||
|
||||
mItemsToPlay = MediaManager.getCurrentVideoQueue();
|
||||
|
||||
if (mItemsToPlay == null || mItemsToPlay.size() == 0) {
|
||||
Utils.showToast(mApplication, mApplication.getString(R.string.msg_no_playable_items));
|
||||
mActivity.finish();
|
||||
@@ -231,14 +231,13 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mApplication.setPlaybackController(new PlaybackController(mItemsToPlay, this));
|
||||
mPlaybackController = mApplication.getPlaybackController();
|
||||
|
||||
//set up fade task
|
||||
// setup fade task
|
||||
mHideTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsVisible) hide();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -246,13 +245,13 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.vlc_player_interface, container);
|
||||
|
||||
// Inject the RowsFragment in the popup container
|
||||
// inject the RowsSupportFragment in the popup container
|
||||
if (getChildFragmentManager().findFragmentById(R.id.rows_area) == null) {
|
||||
mPopupRowsFragment = new RowsFragment();
|
||||
mPopupRowsFragment = new RowsSupportFragment();
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.rows_area, mPopupRowsFragment).commit();
|
||||
} else {
|
||||
mPopupRowsFragment = (RowsFragment) getChildFragmentManager()
|
||||
mPopupRowsFragment = (RowsSupportFragment) getChildFragmentManager()
|
||||
.findFragmentById(R.id.rows_area);
|
||||
}
|
||||
|
||||
@@ -295,12 +294,11 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mTopPanel = mActivity.findViewById(R.id.topPanel);
|
||||
mBottomPanel = mActivity.findViewById(R.id.bottomPanel);
|
||||
mNextUpPanel = mActivity.findViewById(R.id.nextUpPanel);
|
||||
|
||||
mSmNextUpPanel = mActivity.findViewById(R.id.smNextUpPanel);
|
||||
|
||||
mPlayPauseBtn = mActivity.findViewById(R.id.playPauseBtn);
|
||||
mPlayPauseBtn.setSecondaryImage(R.drawable.lb_ic_pause);
|
||||
mPlayPauseBtn.setPrimaryImage(R.drawable.play);
|
||||
mPlayPauseBtn.setSecondaryImage(R.drawable.ic_pause);
|
||||
mPlayPauseBtn.setPrimaryImage(R.drawable.ic_play);
|
||||
mPlayPauseBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -424,7 +422,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mChannels.setFocusable(false);
|
||||
mChannelScroller.setFocusable(false);
|
||||
|
||||
//Register to receive message from popup
|
||||
// register to receive message from popup
|
||||
mActivity.registerMessageListener(new IMessageListener() {
|
||||
@Override
|
||||
public void onMessageReceived(CustomMessage message) {
|
||||
@@ -435,12 +433,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
|
||||
Intent intent = mActivity.getIntent();
|
||||
//start playing
|
||||
int startPos = intent.getIntExtra("Position", 0);
|
||||
|
||||
// start playing
|
||||
mPlaybackController.play(startPos);
|
||||
|
||||
mPlayPauseBtn.requestFocus();
|
||||
|
||||
}
|
||||
|
||||
private void setupPopupAnimations() {
|
||||
@@ -449,7 +447,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
mPopupArea.setVisibility(View.VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -459,14 +456,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
hidePopup = AnimationUtils.loadAnimation(mActivity, R.anim.abc_fade_out);
|
||||
hidePopup.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -477,11 +472,8 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void setupNextUpAnimations() {
|
||||
@@ -490,7 +482,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
mNextUpPanel.setVisibility(View.VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -500,14 +491,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
hideNextUp = AnimationUtils.loadAnimation(mActivity, R.anim.abc_fade_out);
|
||||
hideNextUp.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -517,7 +506,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -526,7 +514,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
mSmNextUpPanel.setVisibility(View.VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -536,14 +523,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
hideSmNextUp = AnimationUtils.loadAnimation(mActivity, R.anim.abc_fade_out);
|
||||
hideSmNextUp.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -553,11 +538,8 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private AudioManager.OnAudioFocusChangeListener mAudioFocusChanged = new AudioManager.OnAudioFocusChangeListener() {
|
||||
@@ -568,7 +550,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mPlaybackController.pause();
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
//We don't do anything here on purpose
|
||||
// We don't do anything here on purpose
|
||||
// On the Nexus we get this notification erroneously when first starting up
|
||||
// and in any instance that we navigate away from our page, we already handle
|
||||
// stopping video and handing back audio focus
|
||||
@@ -583,9 +565,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||
if (item instanceof BaseRowItem) {
|
||||
BaseRowItem rowItem = (BaseRowItem)item;
|
||||
|
||||
switch (rowItem.getItemType()) {
|
||||
|
||||
case Chapter:
|
||||
Long start = rowItem.getChapterInfo().getStartPositionTicks() / 10000;
|
||||
mPlaybackController.seek(start);
|
||||
@@ -603,21 +583,22 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
private View.OnKeyListener keyListener = new View.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP && mActivity != null && !mActivity.isFinishing()) {
|
||||
mActivity.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mPopupPanelVisible && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B || keyCode == KeyEvent.KEYCODE_ESCAPE)) {
|
||||
//back should just hide the popup panel
|
||||
// back should just hide the popup panel
|
||||
hidePopupPanel();
|
||||
if (mPlaybackController.isLiveTv()) hide(); //also close this if live tv
|
||||
|
||||
// also close this if live tv
|
||||
if (mPlaybackController.isLiveTv()) hide();
|
||||
return true;
|
||||
}
|
||||
if (mGuideVisible) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B || keyCode == KeyEvent.KEYCODE_ESCAPE) {
|
||||
//go back to normal
|
||||
// go back to normal
|
||||
hideGuide();
|
||||
return true;
|
||||
} else if ((keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) &&
|
||||
@@ -626,8 +607,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
Utils.beep();
|
||||
switchChannel(mSelectedProgram.getChannelId());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -715,7 +695,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
private void switchChannel(String id) {
|
||||
if (id == null) return;
|
||||
if (mPlaybackController.getCurrentlyPlayingItem().getId().equals(id)) {
|
||||
//same channel, just dismiss overlay
|
||||
// same channel, just dismiss overlay
|
||||
hideGuide();
|
||||
} else {
|
||||
mPlaybackController.stop();
|
||||
@@ -753,7 +733,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
return;
|
||||
}
|
||||
|
||||
//Register a media button receiver so that all media button presses will come to us and not another app
|
||||
// register a media button receiver so that all media button presses will come to us and not another app
|
||||
mAudioManager.registerMediaButtonEventReceiver(new ComponentName(TvApp.getApplication().getPackageName(), RemoteControlReceiver.class.getName()));
|
||||
//TODO implement conditional logic for api 21+
|
||||
|
||||
@@ -773,23 +753,22 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mPlaybackController != null) {
|
||||
if (mPlaybackController.getCurrentlyPlayingItem() != null) RecommendationManager.getInstance().recommend(mPlaybackController.getCurrentlyPlayingItem().getId());
|
||||
mPlaybackController.removePreviousQueueItems();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mPlaybackController.stop();
|
||||
setPlayPauseActionState(ImageButton.STATE_PRIMARY); // in case we come back
|
||||
|
||||
//Give back audio focus
|
||||
// in case we come back
|
||||
setPlayPauseActionState(ImageButton.STATE_PRIMARY);
|
||||
|
||||
// give back audio focus
|
||||
mAudioManager.abandonAudioFocus(mAudioFocusChanged);
|
||||
mApplication.getLogger().Debug("Fragment pausing. IsFinishing: "+mActivity.isFinishing());
|
||||
if (!mActivity.isFinishing()) mActivity.finish(); // user hit "home" we want to back out
|
||||
|
||||
}
|
||||
|
||||
public void show() {
|
||||
@@ -911,8 +890,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
});
|
||||
|
||||
}
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
public void displayChannels(int start, int max) {
|
||||
@@ -941,7 +918,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
DisplayProgramsTask mDisplayProgramsTask;
|
||||
class DisplayProgramsTask extends AsyncTask<Integer, Integer, Void> {
|
||||
|
||||
View firstRow;
|
||||
int displayedChannels = 0;
|
||||
|
||||
@@ -1010,7 +986,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
});
|
||||
|
||||
displayedChannels++;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1036,16 +1011,13 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
mGuideSpinner.setVisibility(View.GONE);
|
||||
if (firstRow != null) firstRow.requestFocus();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private int currentCellId = 0;
|
||||
|
||||
private LinearLayout getProgramRow(List<BaseItemDto> programs, String channelId) {
|
||||
|
||||
LinearLayout programRow = new LinearLayout(mActivity);
|
||||
|
||||
if (programs.size() == 0) {
|
||||
BaseItemDto empty = new BaseItemDto();
|
||||
empty.setName(mApplication.getString(R.string.no_program_data));
|
||||
@@ -1083,7 +1055,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
if (end > getCurrentLocalEndDate()) end = getCurrentLocalEndDate();
|
||||
prevEnd = end;
|
||||
Long duration = (end - start) / 60000;
|
||||
//TvApp.getApplication().getLogger().Debug("Duration for "+item.getName()+" is "+duration.intValue());
|
||||
if (duration > 0) {
|
||||
ProgramGridCell program = new ProgramGridCell(mActivity, mFragment, item);
|
||||
program.setId(currentCellId++);
|
||||
@@ -1091,9 +1062,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
program.setFocusable(true);
|
||||
|
||||
programRow.addView(program);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return programRow;
|
||||
@@ -1124,7 +1093,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
//after first one, we always go on hours
|
||||
interval = 60;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Runnable detailUpdateTask = new Runnable() {
|
||||
@@ -1144,7 +1112,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
detailUpdateInternal();
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
detailUpdateInternal();
|
||||
}
|
||||
@@ -1159,7 +1126,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
//info row
|
||||
InfoLayoutHelper.addInfoRow(mActivity, mSelectedProgram, mGuideInfoRow, false, false);
|
||||
|
||||
} else {
|
||||
mGuideInfoRow.removeAllViews();
|
||||
}
|
||||
@@ -1194,7 +1160,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
private Animation.AnimationListener hideAnimationListener = new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1205,14 +1170,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private Animation.AnimationListener showAnimationListener = new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1223,7 +1186,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1233,9 +1195,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
int width = Utils.convertDpToPixel(getActivity(), 150);
|
||||
String posterImageUrl = ImageUtils.getPrimaryImageUrl(item, mApplication.getApiClient(), false, false, preferSeries, height);
|
||||
if (posterImageUrl != null) Picasso.with(getActivity()).load(posterImageUrl).skipMemoryCache().resize(width, height).centerInside().into(target);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateLogo(BaseItemDto item, ImageView target) {
|
||||
@@ -1268,10 +1228,8 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
if (studioImageUrl != null)
|
||||
Picasso.with(mActivity).load(studioImageUrl).resize(width, height).centerInside().into(mStudioImage);
|
||||
|
||||
} else {
|
||||
mStudioImage.setImageResource(R.drawable.blank30x30);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1287,7 +1245,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void updateCurrentDuration(BaseItemDto item) {
|
||||
@@ -1305,7 +1262,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
if (ndx > 0) {
|
||||
mPopupRowPresenter.setPosition(ndx);
|
||||
}
|
||||
|
||||
}
|
||||
},500);
|
||||
}
|
||||
@@ -1315,7 +1271,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
if (!DeviceUtils.isFireTv() && mPlaybackController.canSeek()) {
|
||||
// on-screen jump buttons for Nexus
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.repeat, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_loop, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mPlaybackController.skip(-11000);
|
||||
@@ -1323,20 +1279,19 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
}));
|
||||
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.fastforward, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_fastforward, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mPlaybackController.skip(30000);
|
||||
startFadeTimer();
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
if (mPlaybackController.isLiveTv()) {
|
||||
// prev channel button
|
||||
// previous channel button
|
||||
if (TvManager.getPrevLiveTvChannel() != null) {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.prev, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_previous_episode, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchChannel(TvManager.getPrevLiveTvChannel());
|
||||
@@ -1344,7 +1299,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}));
|
||||
}
|
||||
|
||||
//Create quick channel change row
|
||||
// create quick channel change row
|
||||
TvManager.loadAllChannels(new Response<Integer>() {
|
||||
@Override
|
||||
public void onResponse(Integer response) {
|
||||
@@ -1353,11 +1308,10 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
if (mChapterRow != null) mPopupRowAdapter.remove(mChapterRow);
|
||||
mChapterRow = new ListRow(new HeaderItem("Channels"), channelAdapter);
|
||||
mPopupRowAdapter.add(mChapterRow);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.channelbar, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_channel_bar, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showQuickChannelChanger();
|
||||
@@ -1365,7 +1319,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}));
|
||||
|
||||
// guide button
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.guidebutton, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_guide, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showGuide();
|
||||
@@ -1374,7 +1328,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
// record button
|
||||
if (item.getCurrentProgram() != null && mApplication.canManageRecordings()) {
|
||||
mButtonRow.addView(new ImageButton(mActivity, item.getCurrentProgram().getTimerId() != null ? R.drawable.rec : R.drawable.recwhite, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, item.getCurrentProgram().getTimerId() != null ? R.drawable.ic_record_red : R.drawable.ic_record, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
toggleRecording(mPlaybackController.getCurrentlyPlayingItem());
|
||||
@@ -1382,15 +1336,14 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}));
|
||||
}
|
||||
|
||||
//adjust popup for channels
|
||||
// adjust popup for channels
|
||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mPopupArea.getLayoutParams();
|
||||
params.height = Utils.convertDpToPixel(mActivity, 170);
|
||||
mPopupArea.setLayoutParams(params);
|
||||
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(item.getOverview())) {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.infoicon, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_info, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showInfo();
|
||||
@@ -1398,12 +1351,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}));
|
||||
}
|
||||
|
||||
Boolean hasSubs = StreamHelper.getSubtitleStreams(mPlaybackController.getCurrentMediaSource()).size() > 0;
|
||||
Boolean hasMultiAudio = StreamHelper.getAudioStreams(mPlaybackController.getCurrentMediaSource()).size() > 1;
|
||||
boolean hasSubs = StreamHelper.getSubtitleStreams(mPlaybackController.getCurrentMediaSource()).size() > 0;
|
||||
boolean hasMultiAudio = StreamHelper.getAudioStreams(mPlaybackController.getCurrentMediaSource()).size() > 1;
|
||||
|
||||
if (hasMultiAudio) {
|
||||
mApplication.getLogger().Debug("Multiple Audio tracks found: "+ StreamHelper.getAudioStreams(mPlaybackController.getCurrentMediaSource()).size());
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.audiosel, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_select_audio, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPlaybackController.getCurrentStreamInfo() == null) {
|
||||
@@ -1420,7 +1373,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
currentAudioIndex = mPlaybackController.translateVlcAudioId(currentAudioIndex);
|
||||
}
|
||||
|
||||
PopupMenu audioMenu = Utils.createPopupMenu(getActivity(), v, Gravity.RIGHT);
|
||||
PopupMenu audioMenu = Utils.createPopupMenu(getActivity(), v, Gravity.END);
|
||||
for (MediaStream audio : audioTracks) {
|
||||
MenuItem item = audioMenu.getMenu().add(0, audio.getIndex(), audio.getIndex(), audio.getDisplayTitle());
|
||||
if (currentAudioIndex != null && currentAudioIndex == audio.getIndex()) item.setChecked(true);
|
||||
@@ -1441,7 +1394,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
});
|
||||
audioMenu.show();
|
||||
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
@@ -1450,7 +1402,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
if (hasSubs) {
|
||||
mApplication.getLogger().Debug("Subtitle tracks found: " + mPlaybackController.getSubtitleStreams().size());
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.subt, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_select_subtitle, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPlaybackController.getCurrentStreamInfo() == null) {
|
||||
@@ -1460,7 +1412,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
setFadingEnabled(false);
|
||||
List<SubtitleStreamInfo> subtitles = mPlaybackController.getSubtitleStreams();
|
||||
PopupMenu subMenu = Utils.createPopupMenu(getActivity(), v, Gravity.RIGHT);
|
||||
PopupMenu subMenu = Utils.createPopupMenu(getActivity(), v, Gravity.END);
|
||||
MenuItem none = subMenu.getMenu().add(0, -1, 0, mApplication.getString(R.string.lbl_none));
|
||||
int currentSubIndex = mPlaybackController.getSubtitleStreamIndex();
|
||||
if (currentSubIndex < 0) none.setChecked(true);
|
||||
@@ -1484,7 +1436,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
});
|
||||
subMenu.show();
|
||||
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
@@ -1493,7 +1444,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
|
||||
List<ChapterInfoDto> chapters = item.getChapters();
|
||||
if (chapters != null && chapters.size() > 0) {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.chapter, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_select_chapter, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showChapterPanel();
|
||||
@@ -1504,13 +1455,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
if (ndx > 0) {
|
||||
mPopupRowPresenter.setPosition(ndx);
|
||||
}
|
||||
|
||||
}
|
||||
},500);
|
||||
}
|
||||
}));
|
||||
|
||||
//Create chapter row for later use
|
||||
// create chapter row for later use
|
||||
ItemRowAdapter chapterAdapter = new ItemRowAdapter(BaseItemUtils.buildChapterItems(item), new CardPresenter(true, 220), new ArrayObjectAdapter());
|
||||
chapterAdapter.Retrieve();
|
||||
if (mChapterRow != null) mPopupRowAdapter.remove(mChapterRow);
|
||||
@@ -1528,7 +1478,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
if (!mPlaybackController.isNativeMode()) {
|
||||
if (mAudioPopup == null ) mAudioPopup = new AudioDelayPopup(mActivity, mBottomPanel, new ValueChangedListener<Long>() {
|
||||
@Override
|
||||
@@ -1536,14 +1485,14 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mPlaybackController.setAudioDelay(value);
|
||||
}
|
||||
});
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.adjust, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_adjust, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAudioPopup.show(mPlaybackController.getAudioDelay());
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.zoom, mButtonSize, new View.OnClickListener() {
|
||||
mButtonRow.addView(new ImageButton(mActivity, R.drawable.ic_zoom, mButtonSize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
PopupMenu zoomMenu = Utils.createPopupMenu(mActivity, v, Gravity.RIGHT);
|
||||
@@ -1571,9 +1520,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
zoomMenu.show();
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int getCurrentChapterIndex(BaseItemDto item, long pos) {
|
||||
@@ -1650,7 +1597,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
|
||||
private void cancelRecording(BaseItemDto program, boolean series) {
|
||||
|
||||
if (program != null) {
|
||||
if (series) {
|
||||
mApplication.getApiClient().CancelLiveTvSeriesTimerAsync(program.getSeriesTimerId(), new EmptyResponse() {
|
||||
@@ -1680,15 +1626,11 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
Utils.showToast(mActivity, R.string.msg_unable_to_cancel);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void recordProgram(final BaseItemDto program, final boolean series) {
|
||||
// Utils.showToast(mActivity, "Not Yet Implemented");
|
||||
// return;
|
||||
|
||||
if (program != null) {
|
||||
mApplication.getApiClient().GetDefaultLiveTvTimerInfo(new Response<SeriesTimerInfoDto>() {
|
||||
@Override
|
||||
@@ -1726,7 +1668,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AudioDelayPopup mAudioPopup;
|
||||
@@ -1782,12 +1723,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
if (mNextUpPanelVisible) hideNextUpPanel();
|
||||
if (mSmNextUpPanelVisible) hideSmNextUpPanel();
|
||||
updateCurrentDuration(current);
|
||||
//set progress to match duration
|
||||
// set progress to match duration
|
||||
mCurrentProgress.setMax(mCurrentDuration);
|
||||
//set other information
|
||||
// set other information
|
||||
mTitle.setText(current.getName());
|
||||
mInfoSummary.setText(current.getOverview());
|
||||
mGuideCurrentTitle.setText(" "+current.getName());
|
||||
mGuideCurrentTitle.setText(current.getName());
|
||||
updatePoster(current, mPoster, true);
|
||||
updateLogo(current, mLogoImage);
|
||||
updateStudio(current);
|
||||
@@ -1798,7 +1739,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
StreamInfo stream = mPlaybackController.getCurrentStreamInfo();
|
||||
if (stream != null) {
|
||||
switch (stream.getPlayMethod()) {
|
||||
|
||||
case Transcode:
|
||||
InfoLayoutHelper.addBlockText(mActivity, mInfoRow, "Trans" + (mPlaybackController.mVideoManager.isNativeMode() ? "/I" : "/V"));
|
||||
break;
|
||||
@@ -1817,7 +1757,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
@Override
|
||||
public void nextItemThresholdHit(BaseItemDto nextItem) {
|
||||
mApplication.getLogger().Debug("Next Item is " + nextItem.getName());
|
||||
//we need to retrieve full item for all info
|
||||
// need to retrieve full item for all info
|
||||
mApplication.getApiClient().GetItemAsync(nextItem.getId(), mApplication.getCurrentUser().getId(), new Response<BaseItemDto>() {
|
||||
@Override
|
||||
public void onResponse(BaseItemDto response) {
|
||||
@@ -1840,10 +1780,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
private long lastReportedPosMs;
|
||||
|
||||
private void updateManualSubtitlePosition() {
|
||||
|
||||
/*
|
||||
* Adjust subtitles margin based on Screen dimes
|
||||
*/
|
||||
// adjust subtitles margin based on screen dimension
|
||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mSubtitleText.getLayoutParams();
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
@@ -1863,7 +1800,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
mSubtitleText.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void showSubLoadingMsg(final boolean show) {
|
||||
@@ -1882,7 +1818,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
|
||||
public void updateSubtitles(long positionMs) {
|
||||
|
||||
if (lastReportedPosMs > 0){
|
||||
if (Math.abs(lastReportedPosMs - positionMs) < 500) {
|
||||
return;
|
||||
@@ -1894,7 +1829,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
|
||||
long positionTicks = positionMs * 10000;
|
||||
|
||||
for (SubtitleTrackEvent caption : mManualSubs.getTrackEvents()) {
|
||||
if (positionTicks >= caption.getStartPositionTicks() && positionTicks <= caption.getEndPositionTicks()) {
|
||||
setTimedText(caption);
|
||||
@@ -1906,7 +1840,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
|
||||
private void setTimedText(final SubtitleTrackEvent textObj) {
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -1916,14 +1849,16 @@ public class CustomPlaybackOverlayFragment extends Fragment implements IPlayback
|
||||
}
|
||||
|
||||
String text = textObj.getText();
|
||||
|
||||
if (text == null || text.length() == 0) {
|
||||
mSubtitleText.setVisibility(View.INVISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
mSubtitleText.setText(Html.fromHtml(text));
|
||||
SpannableString span = new SpannableString(Html.fromHtml(text));
|
||||
span.setSpan(new ForegroundColorSpan(Color.WHITE), 0, span.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
mSubtitleText.setText(span);
|
||||
mSubtitleText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.jellyfin.androidtv.playback;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface;
|
||||
@@ -9,7 +8,6 @@ import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
@@ -19,15 +17,18 @@ import org.jellyfin.androidtv.model.compat.VideoOptions;
|
||||
import org.jellyfin.androidtv.util.ProfileHelper;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.ReportingHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.UserItemDataDto;
|
||||
import org.jellyfin.apiclient.model.session.PlayMethod;
|
||||
|
||||
public class ExternalPlayerActivity extends Activity {
|
||||
import java.util.List;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
|
||||
public class ExternalPlayerActivity extends FragmentActivity {
|
||||
|
||||
List<BaseItemDto> mItemsToPlay;
|
||||
TvApp mApplication = TvApp.getApplication();
|
||||
@@ -133,7 +134,7 @@ public class ExternalPlayerActivity extends Activity {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
SharedPreferences prefs = mApplication.getPrefs();
|
||||
prefs.edit().putBoolean("pref_video_use_external", false).apply();
|
||||
prefs.edit().putString("pref_video_player", "auto").apply();
|
||||
prefs.edit().putBoolean("pref_live_tv_use_external", false).apply();
|
||||
}
|
||||
})
|
||||
@@ -205,7 +206,7 @@ public class ExternalPlayerActivity extends Activity {
|
||||
//Get playback info for current item
|
||||
mCurrentNdx = ndx;
|
||||
final BaseItemDto item = mItemsToPlay.get(mCurrentNdx);
|
||||
isLiveTv = item.getType().equals("TvChannel");
|
||||
isLiveTv = item.getBaseItemType() == BaseItemType.TvChannel;
|
||||
|
||||
if (!isLiveTv && mApplication.getPrefs().getBoolean("pref_send_path_external", false)) {
|
||||
// Just pass the path directly
|
||||
|
||||
@@ -3,9 +3,6 @@ package org.jellyfin.androidtv.playback;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackInfo;
|
||||
|
||||
/**
|
||||
* Created by Eric on 4/28/2015.
|
||||
*/
|
||||
public interface IPlaybackOverlayFragment {
|
||||
void setCurrentTime(long time);
|
||||
void setSecondaryTime(long time);
|
||||
|
||||
@@ -7,14 +7,18 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.text.InputType;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.devbrackets.android.exomedia.EMAudioPlayer;
|
||||
import com.devbrackets.android.exomedia.event.EMMediaProgressEvent;
|
||||
import com.devbrackets.android.exomedia.listener.EMProgressCallback;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
@@ -32,6 +36,12 @@ import org.jellyfin.androidtv.util.RemoteControlReceiver;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.BaseItemUtils;
|
||||
import org.jellyfin.androidtv.util.apiclient.ReportingHelper;
|
||||
import org.jellyfin.apiclient.interaction.ApiClient;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dlna.DeviceProfile;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.playlists.PlaylistCreationRequest;
|
||||
import org.jellyfin.apiclient.model.playlists.PlaylistCreationResult;
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.Media;
|
||||
|
||||
@@ -41,13 +51,6 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.ApiClient;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dlna.DeviceProfile;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.playlists.PlaylistCreationRequest;
|
||||
import org.jellyfin.apiclient.model.playlists.PlaylistCreationResult;
|
||||
|
||||
public class MediaManager {
|
||||
private static ItemRowAdapter mCurrentMediaAdapter;
|
||||
private static int mCurrentMediaPosition = -1;
|
||||
@@ -63,7 +66,7 @@ public class MediaManager {
|
||||
private static LibVLC mLibVLC;
|
||||
private static org.videolan.libvlc.MediaPlayer mVlcPlayer;
|
||||
private static VlcEventHandler mVlcHandler = new VlcEventHandler();
|
||||
private static EMAudioPlayer mExoplayer;
|
||||
private static SimpleExoPlayer mExoPlayer;
|
||||
private static AudioManager mAudioManager;
|
||||
private static boolean audioInitialized;
|
||||
private static boolean nativeMode = false;
|
||||
@@ -157,7 +160,7 @@ public class MediaManager {
|
||||
}
|
||||
|
||||
private static boolean isPaused() {
|
||||
return nativeMode ? !mExoplayer.isPlaying() : !mVlcPlayer.isPlaying();
|
||||
return nativeMode ? !mExoPlayer.isPlaying() : !mVlcPlayer.isPlaying();
|
||||
}
|
||||
|
||||
private static void reportProgress() {
|
||||
@@ -165,7 +168,7 @@ public class MediaManager {
|
||||
if (System.currentTimeMillis() < lastProgressEvent + 750) return;
|
||||
lastProgressEvent = System.currentTimeMillis();
|
||||
|
||||
mCurrentAudioPosition = nativeMode ? mExoplayer.getCurrentPosition() : mVlcPlayer.getTime();
|
||||
mCurrentAudioPosition = nativeMode ? mExoPlayer.getCurrentPosition() : mVlcPlayer.getTime();
|
||||
|
||||
//fire external listeners if there
|
||||
for (AudioEventListener listener : mAudioEventListeners) {
|
||||
@@ -199,19 +202,21 @@ public class MediaManager {
|
||||
// Create a new media player based on platform
|
||||
if (DeviceUtils.is60()) {
|
||||
nativeMode = true;
|
||||
mExoplayer = new EMAudioPlayer(TvApp.getApplication());
|
||||
mExoplayer.setProgressCallback(new EMProgressCallback() {
|
||||
mExoPlayer = ExoPlayerFactory.newSimpleInstance(TvApp.getApplication());
|
||||
mExoPlayer.addListener(new Player.EventListener() {
|
||||
@Override
|
||||
public boolean onProgressUpdated(EMMediaProgressEvent progressEvent) {
|
||||
reportProgress();
|
||||
return false;
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (playbackState == Player.STATE_READY) {
|
||||
startProgressLoop();
|
||||
} else if (playbackState == Player.STATE_ENDED) {
|
||||
onComplete();
|
||||
stopProgressLoop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mExoplayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
onComplete();
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
stopProgressLoop();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -220,17 +225,7 @@ public class MediaManager {
|
||||
options.add("--no-audio-time-stretch");
|
||||
options.add("-v");
|
||||
|
||||
mLibVLC = new LibVLC(options);
|
||||
LibVLC.setOnNativeCrashListener(new LibVLC.OnNativeCrashListener() {
|
||||
@Override
|
||||
public void onNativeCrash() {
|
||||
new Exception().printStackTrace();
|
||||
//todo put our custom log reporter here...
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(10);
|
||||
|
||||
}
|
||||
});
|
||||
mLibVLC = new LibVLC(TvApp.getApplication(), options);
|
||||
|
||||
mVlcPlayer = new org.videolan.libvlc.MediaPlayer(mLibVLC);
|
||||
mVlcPlayer.setAudioOutput(Utils.downMixAudio() ? "opensles_android" : "android_audiotrack");
|
||||
@@ -263,6 +258,25 @@ public class MediaManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Runnable progressLoop;
|
||||
private static Handler mHandler = new Handler();
|
||||
private static void startProgressLoop() {
|
||||
progressLoop = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
reportProgress();
|
||||
mHandler.postDelayed(this, 500);
|
||||
}
|
||||
};
|
||||
mHandler.post(progressLoop);
|
||||
}
|
||||
|
||||
private static void stopProgressLoop() {
|
||||
if (progressLoop != null) {
|
||||
mHandler.removeCallbacks(progressLoop);
|
||||
}
|
||||
}
|
||||
|
||||
private static AudioManager.OnAudioFocusChangeListener mAudioFocusChanged = new AudioManager.OnAudioFocusChangeListener() {
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
@@ -281,6 +295,13 @@ public class MediaManager {
|
||||
}
|
||||
};
|
||||
|
||||
private static void fireQueueReplaced(){
|
||||
for (AudioEventListener listener : mAudioEventListeners) {
|
||||
TvApp.getApplication().getLogger().Info("Firing queue replaced listener. ");
|
||||
listener.onQueueReplaced();
|
||||
}
|
||||
}
|
||||
|
||||
private static void fireQueueStatusChange() {
|
||||
for (AudioEventListener listener : mAudioEventListeners) {
|
||||
TvApp.getApplication().getLogger().Info("Firing queue state change listener. "+ hasAudioQueueItems());
|
||||
@@ -370,7 +391,7 @@ public class MediaManager {
|
||||
public static int queueAudioItem(int pos, BaseItemDto item) {
|
||||
if (mCurrentAudioQueue == null) createAudioQueue(new ArrayList<BaseItemDto>());
|
||||
mCurrentAudioQueue.add(new BaseRowItem(pos, item));
|
||||
TvApp.getApplication().showMessage(TvApp.getApplication().getString(R.string.msg_added_item_to_queue) + (pos + 1), BaseItemUtils.getFullName(item), 4000, R.drawable.audioicon);
|
||||
TvApp.getApplication().showMessage(TvApp.getApplication().getString(R.string.msg_added_item_to_queue) + (pos + 1), BaseItemUtils.getFullName(item), 4000, R.drawable.ic_album);
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -421,7 +442,7 @@ public class MediaManager {
|
||||
}
|
||||
fireQueueStatusChange();
|
||||
}
|
||||
TvApp.getApplication().showMessage(items.size() + (items.size() > 1 ? TvApp.getApplication().getString(R.string.msg_items_added) : TvApp.getApplication().getString(R.string.msg_item_added)), mCurrentAudioQueue.size() + TvApp.getApplication().getString(R.string.msg_total_items_in_queue), 5000, R.drawable.audioicon);
|
||||
TvApp.getApplication().showMessage(items.size() + (items.size() > 1 ? TvApp.getApplication().getString(R.string.msg_items_added) : TvApp.getApplication().getString(R.string.msg_item_added)), mCurrentAudioQueue.size() + TvApp.getApplication().getString(R.string.msg_total_items_in_queue), 5000, R.drawable.ic_album);
|
||||
}
|
||||
|
||||
public static void removeFromAudioQueue(int ndx) {
|
||||
@@ -457,7 +478,7 @@ public class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPlayingAudio() { return audioInitialized && (nativeMode ? mExoplayer.isPlaying() : mVlcPlayer.isPlaying()); }
|
||||
public static boolean isPlayingAudio() { return audioInitialized && (nativeMode ? mExoPlayer.isPlaying() : mVlcPlayer.isPlaying()); }
|
||||
|
||||
private static boolean ensureInitialized() {
|
||||
if (!audioInitialized) {
|
||||
@@ -482,6 +503,7 @@ public class MediaManager {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
playNowInternal(items);
|
||||
fireQueueReplaced();
|
||||
}
|
||||
})
|
||||
.setNeutralButton(R.string.lbl_add_to_queue, new DialogInterface.OnClickListener() {
|
||||
@@ -495,7 +517,6 @@ public class MediaManager {
|
||||
} else {
|
||||
playNowInternal(items);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void playNowInternal(List<BaseItemDto> items) {
|
||||
@@ -506,7 +527,7 @@ public class MediaManager {
|
||||
Intent nowPlaying = new Intent(TvApp.getApplication(), AudioNowPlayingActivity.class);
|
||||
TvApp.getApplication().getCurrentActivity().startActivity(nowPlaying);
|
||||
} else {
|
||||
TvApp.getApplication().showMessage(items.size() + (items.size() > 1 ? TvApp.getApplication().getString(R.string.msg_items_added) : TvApp.getApplication().getString(R.string.msg_item_added)), mCurrentAudioQueue.size() + TvApp.getApplication().getString(R.string.msg_total_items_in_queue), 5000, R.drawable.audioicon);
|
||||
TvApp.getApplication().showMessage(items.size() + (items.size() > 1 ? TvApp.getApplication().getString(R.string.msg_items_added) : TvApp.getApplication().getString(R.string.msg_item_added)), mCurrentAudioQueue.size() + TvApp.getApplication().getString(R.string.msg_total_items_in_queue), 5000, R.drawable.ic_album);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -567,8 +588,10 @@ public class MediaManager {
|
||||
mCurrentAudioQueuePosition = pos;
|
||||
mCurrentAudioPosition = 0;
|
||||
if (nativeMode) {
|
||||
mExoplayer.setDataSource(TvApp.getApplication(), Uri.parse(response.ToUrl(apiClient.getApiUrl(), apiClient.getAccessToken())));
|
||||
mExoplayer.start();
|
||||
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(TvApp.getApplication(), "ATV/ExoPlayer");
|
||||
|
||||
mExoPlayer.setPlayWhenReady(true);
|
||||
mExoPlayer.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(response.ToUrl(apiClient.getApiUrl(), apiClient.getAccessToken()))));
|
||||
} else {
|
||||
TvApp.getApplication().getLogger().Info("Playback attempt via VLC of " + response.getMediaUrl());
|
||||
Media media = new Media(mLibVLC, Uri.parse(response.getMediaUrl()));
|
||||
@@ -670,7 +693,7 @@ public class MediaManager {
|
||||
if (mCurrentAudioQueue == null || (!mRepeat && mCurrentAudioQueue.size() == 0)) return -1;
|
||||
if (isPlayingAudio() && mCurrentAudioPosition > 10000) {
|
||||
//just back up to the beginning of current item
|
||||
if (nativeMode) mExoplayer.seekTo(0);
|
||||
if (nativeMode) mExoPlayer.seekTo(0);
|
||||
else mVlcPlayer.setTime(0);
|
||||
return mCurrentAudioQueuePosition;
|
||||
}
|
||||
@@ -692,7 +715,7 @@ public class MediaManager {
|
||||
}
|
||||
|
||||
private static void stop() {
|
||||
if (nativeMode) mExoplayer.stopPlayback();
|
||||
if (nativeMode) mExoPlayer.stop(true);
|
||||
else mVlcPlayer.stop();
|
||||
}
|
||||
|
||||
@@ -711,7 +734,7 @@ public class MediaManager {
|
||||
}
|
||||
|
||||
private static void pause() {
|
||||
if (nativeMode) mExoplayer.pause();
|
||||
if (nativeMode) mExoPlayer.setPlayWhenReady(false);
|
||||
else mVlcPlayer.pause();
|
||||
}
|
||||
|
||||
@@ -733,7 +756,7 @@ public class MediaManager {
|
||||
public static void resumeAudio() {
|
||||
if (mCurrentAudioItem != null) {
|
||||
ensureAudioFocus();
|
||||
if (nativeMode) mExoplayer.start();
|
||||
if (nativeMode) mExoPlayer.setPlayWhenReady(true);
|
||||
else mVlcPlayer.play();
|
||||
updateCurrentAudioItemPlaying(true);
|
||||
ReportingHelper.reportStart(mCurrentAudioItem, mCurrentAudioPosition * 10000);
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.view.WindowManager;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.constants.ContainerTypes;
|
||||
import org.jellyfin.androidtv.livetv.TvManager;
|
||||
import org.jellyfin.androidtv.model.compat.PlaybackException;
|
||||
import org.jellyfin.androidtv.model.compat.StreamInfo;
|
||||
@@ -23,26 +24,39 @@ import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.PlaybackHelper;
|
||||
import org.jellyfin.androidtv.util.apiclient.ReportingHelper;
|
||||
import org.jellyfin.androidtv.util.apiclient.StreamHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.ApiClient;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dlna.DeviceProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.DirectPlayProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.SubtitleDeliveryMethod;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemDto;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
|
||||
import org.jellyfin.apiclient.model.entities.LocationType;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStream;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStreamType;
|
||||
import org.jellyfin.apiclient.model.extensions.StringHelper;
|
||||
import org.jellyfin.apiclient.model.library.PlayAccess;
|
||||
import org.jellyfin.apiclient.model.livetv.ChannelInfoDto;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackInfo;
|
||||
import org.jellyfin.apiclient.model.session.PlayMethod;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PlaybackController {
|
||||
// Frequency to report playback progress
|
||||
private final static long PROGRESS_REPORTING_INTERVAL = TimeUtils.secondsToMillis(3);
|
||||
// Frequency to report paused state
|
||||
private static final long PROGRESS_REPORTING_PAUSE_INTERVAL = TimeUtils.secondsToMillis(15);
|
||||
|
||||
// Minimum duration to show next up popup
|
||||
private static final long NEXT_UP_MIN_LENGTH = TimeUtils.minutesToMillis(10);
|
||||
// Minimum duration to show next up popup for longer
|
||||
private static final long NEXT_UP_LONG_LENGTH = TimeUtils.hoursToMillis(1.25);
|
||||
// The duration to display the next up popup for longer shows
|
||||
private static final long NEXT_UP_LONG_DURATION = TimeUtils.minutesToMillis(3);
|
||||
// The default duration to display the next up popup
|
||||
private static final long NEXT_UP_DURATION = TimeUtils.secondsToMillis(30);
|
||||
|
||||
List<BaseItemDto> mItems;
|
||||
VideoManager mVideoManager;
|
||||
SubtitleHelper mSubHelper;
|
||||
@@ -66,7 +80,6 @@ public class PlaybackController {
|
||||
|
||||
private Runnable mReportLoop;
|
||||
private Handler mHandler;
|
||||
private static int REPORT_INTERVAL = 3000;
|
||||
|
||||
private long mNextItemThreshold = Long.MAX_VALUE;
|
||||
private boolean nextItemReported;
|
||||
@@ -77,7 +90,7 @@ public class PlaybackController {
|
||||
private boolean isLiveTv;
|
||||
private boolean directStreamLiveTv;
|
||||
private String liveTvChannelName = "";
|
||||
private boolean useVlc = false;
|
||||
private boolean useVlc;
|
||||
|
||||
private boolean vlcErrorEncountered;
|
||||
private boolean exoErrorEncountered;
|
||||
@@ -98,6 +111,9 @@ public class PlaybackController {
|
||||
refreshRateSwitchingEnabled = DeviceUtils.is60() && mApplication.getPrefs().getBoolean("pref_refresh_switching", false);
|
||||
if (refreshRateSwitchingEnabled) getDisplayModes();
|
||||
|
||||
// Set default value for useVlc field
|
||||
// when set to auto the default will be exoplayer
|
||||
useVlc = mApplication.getPrefs().getString("pref_video_player", "auto").equals("vlc");
|
||||
}
|
||||
|
||||
public void init(VideoManager mgr, View spinner) {
|
||||
@@ -318,7 +334,7 @@ public class PlaybackController {
|
||||
return;
|
||||
}
|
||||
|
||||
isLiveTv = item.getType().equals("TvChannel");
|
||||
isLiveTv = item.getBaseItemType() == BaseItemType.TvChannel;
|
||||
startSpinner();
|
||||
|
||||
//Build options for each player
|
||||
@@ -374,17 +390,17 @@ public class PlaybackController {
|
||||
|
||||
if (hasNextItem()) {
|
||||
// Determine the "next up" threshold
|
||||
if (duration > 600000) {
|
||||
if (duration > NEXT_UP_MIN_LENGTH) {
|
||||
//only items longer than 10min to have this feature
|
||||
nextItemReported = false;
|
||||
if (duration > 4500000) {
|
||||
if (duration > NEXT_UP_LONG_LENGTH) {
|
||||
//longer than 1hr 15 it probably has pretty long credits
|
||||
mNextItemThreshold = duration - 180000; // 3 min
|
||||
mNextItemThreshold = duration - NEXT_UP_LONG_DURATION;
|
||||
} else {
|
||||
//std 30 min episode or less
|
||||
mNextItemThreshold = duration - 50000; // 50 seconds
|
||||
mNextItemThreshold = duration - NEXT_UP_DURATION;
|
||||
}
|
||||
TvApp.getApplication().getLogger().Debug("Next item threshold set to "+ mNextItemThreshold);
|
||||
TvApp.getApplication().getLogger().Debug("Next item threshold set to " + mNextItemThreshold);
|
||||
} else {
|
||||
mNextItemThreshold = Long.MAX_VALUE;
|
||||
}
|
||||
@@ -454,27 +470,44 @@ public class PlaybackController {
|
||||
@Override
|
||||
public void onResponse(StreamInfo internalResponse) {
|
||||
mApplication.getLogger().Info("Internal player would " + (internalResponse.getPlayMethod().equals(PlayMethod.Transcode) ? "transcode" : "direct stream"));
|
||||
boolean useDeinterlacing = vlcResponse.getMediaSource().getVideoStream().getIsInterlaced() && (vlcResponse.getMediaSource().getVideoStream().getWidth() == null || vlcResponse.getMediaSource().getVideoStream().getWidth() > 1200);
|
||||
boolean useDeinterlacing = vlcResponse.getMediaSource().getVideoStream() != null &&
|
||||
vlcResponse.getMediaSource().getVideoStream().getIsInterlaced() &&
|
||||
(vlcResponse.getMediaSource().getVideoStream().getWidth() == null ||
|
||||
vlcResponse.getMediaSource().getVideoStream().getWidth() > 1200);
|
||||
mApplication.getLogger().Info(useDeinterlacing ? "Explicit deinterlacing will be used" : "Explicit deinterlacing will NOT be used");
|
||||
|
||||
// TODO: Clean up this logic
|
||||
// Now look at both responses and choose the one that direct plays or bitstreams - favor VLC
|
||||
useVlc = !vlcErrorEncountered &&
|
||||
!vlcResponse.getPlayMethod().equals(PlayMethod.Transcode) &&
|
||||
(DeviceUtils.is60() ||
|
||||
!mApplication.getPrefs().getBoolean("pref_bitstream_ac3", false) ||
|
||||
(!"ac3".equals(vlcResponse.getMediaSource().getDefaultAudioStream().getCodec()) &&
|
||||
!"truehd".equals(vlcResponse.getMediaSource().getDefaultAudioStream().getCodec()))) &&
|
||||
(Utils.downMixAudio() ||
|
||||
!DeviceUtils.is60() ||
|
||||
internalResponse.getPlayMethod().equals(PlayMethod.Transcode) ||
|
||||
!mApplication.getPrefs().getBoolean("pref_bitstream_dts", false) ||
|
||||
internalResponse.getMediaSource() == null ||
|
||||
internalResponse.getMediaSource().getDefaultAudioStream() == null ||
|
||||
(!internalResponse.getMediaSource().getDefaultAudioStream().getCodec().equals("dca") &&
|
||||
!internalResponse.getMediaSource().getDefaultAudioStream().getCodec().equals("dts"))) &&
|
||||
(!DeviceUtils.isFireTvStick() |
|
||||
(vlcResponse.getMediaSource().getVideoStream() != null && vlcResponse.getMediaSource().getVideoStream().getWidth() < 1000));
|
||||
String preferredVideoPlayer = mApplication.getPrefs().getString("pref_video_player", "auto");
|
||||
|
||||
mApplication.getLogger().Info("User preferred player is: " + preferredVideoPlayer);
|
||||
|
||||
if (preferredVideoPlayer.equals("vlc")) {
|
||||
// Force VLC
|
||||
useVlc = true;
|
||||
} else if (preferredVideoPlayer.equals("exoplayer")) {
|
||||
// Make sure to not use VLC
|
||||
useVlc = false;
|
||||
} else if (preferredVideoPlayer.equals("auto")) {
|
||||
// TODO: Clean up this logic
|
||||
// Now look at both responses and choose the one that direct plays or bitstreams - favor VLC
|
||||
useVlc = !vlcErrorEncountered &&
|
||||
!vlcResponse.getPlayMethod().equals(PlayMethod.Transcode) &&
|
||||
(DeviceUtils.is60() ||
|
||||
!mApplication.getPrefs().getBoolean("pref_bitstream_ac3", false) ||
|
||||
vlcResponse.getMediaSource() == null ||
|
||||
vlcResponse.getMediaSource().getDefaultAudioStream() == null ||
|
||||
(!"ac3".equals(vlcResponse.getMediaSource().getDefaultAudioStream().getCodec()) &&
|
||||
!"truehd".equals(vlcResponse.getMediaSource().getDefaultAudioStream().getCodec()))) &&
|
||||
(Utils.downMixAudio() ||
|
||||
!DeviceUtils.is60() ||
|
||||
internalResponse.getPlayMethod().equals(PlayMethod.Transcode) ||
|
||||
!mApplication.getPrefs().getBoolean("pref_bitstream_dts", false) ||
|
||||
internalResponse.getMediaSource() == null ||
|
||||
internalResponse.getMediaSource().getDefaultAudioStream() == null ||
|
||||
(!internalResponse.getMediaSource().getDefaultAudioStream().getCodec().equals("dca") &&
|
||||
!internalResponse.getMediaSource().getDefaultAudioStream().getCodec().equals("dts"))) &&
|
||||
(!DeviceUtils.isFireTvStick() ||
|
||||
(vlcResponse.getMediaSource().getVideoStream() != null && vlcResponse.getMediaSource().getVideoStream().getWidth() < 1000));
|
||||
}
|
||||
|
||||
mApplication.getLogger().Info(useVlc ? "Preferring VLC" : "Will use internal player");
|
||||
mVideoManager.init(getBufferAmount(), useDeinterlacing);
|
||||
@@ -506,7 +539,6 @@ public class PlaybackController {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
mApplication.getLogger().ErrorException("Unable to get internal stream info", exception);
|
||||
useVlc = true;
|
||||
mCurrentOptions = vlcOptions;
|
||||
startItem(item, position, vlcResponse);
|
||||
}
|
||||
@@ -550,7 +582,10 @@ public class PlaybackController {
|
||||
|
||||
setPlaybackMethod(response.getPlayMethod());
|
||||
|
||||
if (useVlc && (!getPlaybackMethod().equals(PlayMethod.Transcode) || isLiveTv)) {
|
||||
// Force VLC when media is not live TV and the preferred player is VLC
|
||||
boolean forceVlc = !isLiveTv && mApplication.getPrefs().getString("pref_video_player", "auto").equals("vlc");
|
||||
|
||||
if (forceVlc || (useVlc && (!getPlaybackMethod().equals(PlayMethod.Transcode) || isLiveTv))) {
|
||||
TvApp.getApplication().getLogger().Info("Playing back in VLC.");
|
||||
mVideoManager.setNativeMode(false);
|
||||
} else {
|
||||
@@ -726,7 +761,7 @@ public class PlaybackController {
|
||||
mVideoManager.disableSubs();
|
||||
mFragment.showSubLoadingMsg(true);
|
||||
stream.setDeliveryMethod(SubtitleDeliveryMethod.External);
|
||||
stream.setDeliveryUrl(String.format("%1$s/Videos/%2$s/%3$s/Subtitles/%4$s/0/Stream.JSON", mApplication.getApiClient().getApiUrl(), mCurrentStreamInfo.getItemId(), mCurrentStreamInfo.getMediaSourceId(), StringHelper.ToStringCultureInvariant(stream.getIndex())));
|
||||
stream.setDeliveryUrl(String.format("%1$s/Videos/%2$s/%3$s/Subtitles/%4$s/0/Stream.JSON", mApplication.getApiClient().getApiUrl(), mCurrentStreamInfo.getItemId(), mCurrentStreamInfo.getMediaSourceId(), String.valueOf(stream.getIndex())));
|
||||
mApplication.getApiClient().getSubtitles(stream.getDeliveryUrl(), new Response<SubtitleTrackInfo>() {
|
||||
|
||||
@Override
|
||||
@@ -831,7 +866,7 @@ public class PlaybackController {
|
||||
public void seek(final long pos) {
|
||||
mApplication.getLogger().Debug("Seeking to " + pos);
|
||||
mApplication.getLogger().Debug("Container: "+mCurrentStreamInfo.getContainer());
|
||||
if (mPlaybackMethod == PlayMethod.Transcode && "mkv".equals(mCurrentStreamInfo.getContainer())) {
|
||||
if (mPlaybackMethod == PlayMethod.Transcode && ContainerTypes.MKV.equals(mCurrentStreamInfo.getContainer())) {
|
||||
//mkv transcodes require re-start of stream for seek
|
||||
mVideoManager.stopPlayback();
|
||||
mApplication.getPlaybackManager().changeVideoStream(mCurrentStreamInfo, mApplication.getApiClient().getServerInfo().getId(), mCurrentOptions, pos * 10000, mApplication.getApiClient(), new Response<StreamInfo>() {
|
||||
@@ -849,11 +884,10 @@ public class PlaybackController {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (mVideoManager.isNativeMode() && !isLiveTv && "ts".equals(mCurrentStreamInfo.getContainer())) {
|
||||
if (mVideoManager.isNativeMode() && !isLiveTv && ContainerTypes.TS.equals(mCurrentStreamInfo.getContainer())) {
|
||||
//Exo does not support seeking in .ts
|
||||
Utils.showToast(TvApp.getApplication(), "Unable to seek");
|
||||
} else
|
||||
if (mVideoManager.seekTo(pos) >= 0) {
|
||||
} else if (mVideoManager.seekTo(pos) >= 0) {
|
||||
if (mFragment != null) {
|
||||
mFragment.updateEndTime(mVideoManager.getDuration() - pos);
|
||||
}
|
||||
@@ -903,7 +937,7 @@ public class PlaybackController {
|
||||
public void updateTvProgramInfo() {
|
||||
// Get the current program info when playing a live TV channel
|
||||
final BaseItemDto channel = getCurrentlyPlayingItem();
|
||||
if (channel.getType().equals("TvChannel")) {
|
||||
if (channel.getBaseItemType() == BaseItemType.TvChannel) {
|
||||
TvApp.getApplication().getApiClient().GetLiveTvChannelAsync(channel.getId(), TvApp.getApplication().getCurrentUser().getId(), new Response<ChannelInfoDto>() {
|
||||
@Override
|
||||
public void onResponse(ChannelInfoDto response) {
|
||||
@@ -945,16 +979,18 @@ public class PlaybackController {
|
||||
ReportingHelper.reportProgress(getCurrentlyPlayingItem(), getCurrentStreamInfo(), currentTime * 10000, false);
|
||||
|
||||
//Do this next up processing here because every 3 seconds is good enough
|
||||
if (!nextItemReported && hasNextItem() && currentTime >= mNextItemThreshold){
|
||||
if (!nextItemReported && hasNextItem() && currentTime >= mNextItemThreshold) {
|
||||
nextItemReported = true;
|
||||
mFragment.nextItemThresholdHit(getNextItem());
|
||||
}
|
||||
}
|
||||
mApplication.setLastUserInteraction(System.currentTimeMillis());
|
||||
if (mPlaybackState != PlaybackState.UNDEFINED && mPlaybackState != PlaybackState.IDLE) mHandler.postDelayed(this, REPORT_INTERVAL);
|
||||
if (mPlaybackState != PlaybackState.UNDEFINED && mPlaybackState != PlaybackState.IDLE) {
|
||||
mHandler.postDelayed(this, PROGRESS_REPORTING_INTERVAL);
|
||||
}
|
||||
}
|
||||
};
|
||||
mHandler.postDelayed(mReportLoop, REPORT_INTERVAL);
|
||||
mHandler.postDelayed(mReportLoop, PROGRESS_REPORTING_INTERVAL);
|
||||
}
|
||||
|
||||
private void startPauseReportLoop() {
|
||||
@@ -964,13 +1000,15 @@ public class PlaybackController {
|
||||
public void run() {
|
||||
|
||||
long currentTime = isLiveTv ? getTimeShiftedProgress() : mVideoManager.getCurrentPosition();
|
||||
if (isLiveTv && !directStreamLiveTv) mFragment.setSecondaryTime(getRealTimeProgress());
|
||||
if (isLiveTv && !directStreamLiveTv) {
|
||||
mFragment.setSecondaryTime(getRealTimeProgress());
|
||||
}
|
||||
|
||||
ReportingHelper.reportProgress(getCurrentlyPlayingItem(), getCurrentStreamInfo(), currentTime * 10000, true);
|
||||
mHandler.postDelayed(this, 15000);
|
||||
mHandler.postDelayed(this, PROGRESS_REPORTING_PAUSE_INTERVAL);
|
||||
}
|
||||
};
|
||||
mHandler.postDelayed(mReportLoop, 15000);
|
||||
mHandler.postDelayed(mReportLoop, PROGRESS_REPORTING_PAUSE_INTERVAL);
|
||||
}
|
||||
|
||||
private void stopReportLoop() {
|
||||
@@ -978,7 +1016,6 @@ public class PlaybackController {
|
||||
mHandler.removeCallbacks(mReportLoop);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void delayedSeek(final long position) {
|
||||
@@ -1081,8 +1118,14 @@ public class PlaybackController {
|
||||
} else {
|
||||
if (mDefaultSubIndex >= 0) {
|
||||
//Default subs requested select them
|
||||
mApplication.getLogger().Info("Selecting default sub stream: " + mDefaultSubIndex);
|
||||
switchSubtitleStream(mDefaultSubIndex);
|
||||
Integer currentIndex = mCurrentOptions.getSubtitleStreamIndex();
|
||||
|
||||
if (currentIndex != null && currentIndex == mDefaultSubIndex) {
|
||||
mApplication.getLogger().Info("Not selecting default subtitle stream because it is already selected");
|
||||
} else {
|
||||
mApplication.getLogger().Info("Selecting default sub stream: " + mDefaultSubIndex);
|
||||
switchSubtitleStream(mDefaultSubIndex);
|
||||
}
|
||||
} else {
|
||||
TvApp.getApplication().getLogger().Info("Turning off subs by default");
|
||||
mVideoManager.disableSubs();
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
package org.jellyfin.androidtv.playback;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.devbrackets.android.exomedia.EMVideoView;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.text.TextOutput;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.util.DeviceUtils;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStream;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStreamType;
|
||||
import org.videolan.libvlc.IVLCVout;
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.Media;
|
||||
@@ -25,14 +40,7 @@ import org.videolan.libvlc.Media;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.model.dto.MediaSourceInfo;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStream;
|
||||
import org.jellyfin.apiclient.model.entities.MediaStreamType;
|
||||
|
||||
/**
|
||||
* Created by Eric on 7/11/2015.
|
||||
*/
|
||||
public class VideoManager implements IVLCVout.Callback {
|
||||
public class VideoManager implements IVLCVout.OnNewVideoLayoutListener {
|
||||
|
||||
public final static int ZOOM_NORMAL = 0;
|
||||
public final static int ZOOM_VERTICAL = 1;
|
||||
@@ -46,7 +54,8 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
private SurfaceView mSurfaceView;
|
||||
private SurfaceView mSubtitlesSurface;
|
||||
private FrameLayout mSurfaceFrame;
|
||||
private EMVideoView mVideoView;
|
||||
private SimpleExoPlayer mExoPlayer;
|
||||
private PlayerView mExoPlayerView;
|
||||
private LibVLC mLibVLC;
|
||||
private org.videolan.libvlc.MediaPlayer mVlcPlayer;
|
||||
private String mCurrentVideoPath;
|
||||
@@ -66,19 +75,23 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
private long mForcedTime = -1;
|
||||
private long mLastTime = -1;
|
||||
private long mMetaDuration = -1;
|
||||
private long lastExoPlayerPosition = -1;
|
||||
|
||||
private boolean nativeMode = false;
|
||||
private boolean mSurfaceReady = false;
|
||||
public boolean isContracted = false;
|
||||
private boolean hasSubtitlesSurface = false;
|
||||
private PlaybackListener errorListener;
|
||||
private PlaybackListener completionListener;
|
||||
private PlaybackListener preparedListener;
|
||||
|
||||
public VideoManager(PlaybackOverlayActivity activity, View view) {
|
||||
mActivity = activity;
|
||||
mSurfaceView = (SurfaceView) view.findViewById(R.id.player_surface);
|
||||
mSurfaceView = view.findViewById(R.id.player_surface);
|
||||
mSurfaceHolder = mSurfaceView.getHolder();
|
||||
mSurfaceHolder.addCallback(mSurfaceCallback);
|
||||
mSurfaceFrame = (FrameLayout) view.findViewById(R.id.player_surface_frame);
|
||||
mSubtitlesSurface = (SurfaceView) view.findViewById(R.id.subtitles_surface);
|
||||
mSurfaceFrame = view.findViewById(R.id.player_surface_frame);
|
||||
mSubtitlesSurface = view.findViewById(R.id.subtitles_surface);
|
||||
if (DeviceUtils.is50()) {
|
||||
mSubtitlesSurface.setZOrderMediaOverlay(true);
|
||||
mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT);
|
||||
@@ -86,8 +99,41 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
} else {
|
||||
mSubtitlesSurface.setVisibility(View.GONE);
|
||||
}
|
||||
mVideoView = (EMVideoView) view.findViewById(R.id.videoView);
|
||||
|
||||
mExoPlayer = ExoPlayerFactory.newSimpleInstance(
|
||||
TvApp.getApplication(),
|
||||
new DefaultRenderersFactory(TvApp.getApplication()) {
|
||||
@Override
|
||||
protected void buildTextRenderers(Context context,
|
||||
TextOutput output,
|
||||
Looper outputLooper, int extensionRendererMode,
|
||||
ArrayList<Renderer> out) {
|
||||
// Do not add text renderers since we handle subtitles
|
||||
}
|
||||
},
|
||||
new DefaultTrackSelector());
|
||||
mExoPlayerView = view.findViewById(R.id.exoPlayerView);
|
||||
mExoPlayerView.setPlayer(mExoPlayer);
|
||||
mExoPlayer.addListener(new Player.EventListener() {
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
TvApp.getApplication().getLogger().Error("***** Got error from player");
|
||||
if (errorListener != null) errorListener.onEvent();
|
||||
stopProgressLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
// Do not call listener when paused
|
||||
if (playbackState == Player.STATE_READY && playWhenReady) {
|
||||
if (preparedListener != null) preparedListener.onEvent();
|
||||
startProgressLoop();
|
||||
} else if (playbackState == Player.STATE_ENDED) {
|
||||
if (completionListener != null) completionListener.onEvent();
|
||||
stopProgressLoop();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void init(int buffer, boolean isInterlaced) {
|
||||
@@ -97,9 +143,9 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
public void setNativeMode(boolean value) {
|
||||
nativeMode = value;
|
||||
if (nativeMode) {
|
||||
mVideoView.setVisibility(View.VISIBLE);
|
||||
mExoPlayerView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mVideoView.setVisibility(View.GONE);
|
||||
mExoPlayerView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,20 +156,20 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
mZoomMode = mode;
|
||||
switch (mode) {
|
||||
case ZOOM_NORMAL:
|
||||
mVideoView.setScaleY(1);
|
||||
mVideoView.setScaleX(1);
|
||||
mExoPlayerView.setScaleY(1);
|
||||
mExoPlayerView.setScaleX(1);
|
||||
break;
|
||||
case ZOOM_VERTICAL:
|
||||
mVideoView.setScaleX(1);
|
||||
mVideoView.setScaleY(1.33f);
|
||||
mExoPlayerView.setScaleX(1);
|
||||
mExoPlayerView.setScaleY(1.33f);
|
||||
break;
|
||||
case ZOOM_HORIZONTAL:
|
||||
mVideoView.setScaleY(1);
|
||||
mVideoView.setScaleX(1.33f);
|
||||
mExoPlayerView.setScaleY(1);
|
||||
mExoPlayerView.setScaleX(1.33f);
|
||||
break;
|
||||
case ZOOM_FULL:
|
||||
mVideoView.setScaleX(1.33f);
|
||||
mVideoView.setScaleY(1.33f);
|
||||
mExoPlayerView.setScaleX(1.33f);
|
||||
mExoPlayerView.setScaleY(1.33f);
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -135,14 +181,22 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public long getDuration() {
|
||||
if (nativeMode){
|
||||
return mVideoView.getDuration() > 0 ? mVideoView.getDuration() : mMetaDuration;
|
||||
return mExoPlayer.getDuration() > 0 ? mExoPlayer.getDuration() : mMetaDuration;
|
||||
} else {
|
||||
return mVlcPlayer.getLength() > 0 ? mVlcPlayer.getLength() : mMetaDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public long getCurrentPosition() {
|
||||
if (nativeMode) return mVideoView.getCurrentPosition();
|
||||
if (nativeMode) {
|
||||
if (mExoPlayer == null) {
|
||||
return lastExoPlayerPosition;
|
||||
} else {
|
||||
long mExoPlayerCurrentPosition = mExoPlayer.getCurrentPosition();
|
||||
lastExoPlayerPosition = mExoPlayerCurrentPosition;
|
||||
return mExoPlayerCurrentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (mVlcPlayer == null) return 0;
|
||||
|
||||
@@ -166,17 +220,17 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return nativeMode ? mVideoView.isPlaying() : mVlcPlayer != null && mVlcPlayer.isPlaying();
|
||||
return nativeMode ? mExoPlayer.isPlaying() : mVlcPlayer != null && mVlcPlayer.isPlaying();
|
||||
}
|
||||
|
||||
public boolean canSeek() { return nativeMode || mVlcPlayer.isSeekable(); }
|
||||
|
||||
public void start() {
|
||||
if (nativeMode) {
|
||||
mVideoView.start();
|
||||
mVideoView.setKeepScreenOn(true);
|
||||
normalWidth = mVideoView.getLayoutParams().width;
|
||||
normalHeight = mVideoView.getLayoutParams().height;
|
||||
mExoPlayer.setPlayWhenReady(true);
|
||||
mExoPlayerView.setKeepScreenOn(true);
|
||||
normalWidth = mExoPlayerView.getLayoutParams().width;
|
||||
normalHeight = mExoPlayerView.getLayoutParams().height;
|
||||
} else {
|
||||
if (!mSurfaceReady) {
|
||||
TvApp.getApplication().getLogger().Error("Attempt to play before surface ready");
|
||||
@@ -192,8 +246,8 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public void play() {
|
||||
if (nativeMode) {
|
||||
mVideoView.start();
|
||||
mVideoView.setKeepScreenOn(true);
|
||||
mExoPlayer.setPlayWhenReady(true);
|
||||
mExoPlayerView.setKeepScreenOn(true);
|
||||
} else {
|
||||
mVlcPlayer.play();
|
||||
mSurfaceView.setKeepScreenOn(true);
|
||||
@@ -202,8 +256,8 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public void pause() {
|
||||
if (nativeMode) {
|
||||
mVideoView.pause();
|
||||
mVideoView.setKeepScreenOn(false);
|
||||
mExoPlayer.setPlayWhenReady(false);
|
||||
mExoPlayerView.setKeepScreenOn(false);
|
||||
} else {
|
||||
mVlcPlayer.pause();
|
||||
mSurfaceView.setKeepScreenOn(false);
|
||||
@@ -217,7 +271,7 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public void stopPlayback() {
|
||||
if (nativeMode) {
|
||||
mVideoView.stopPlayback();
|
||||
mExoPlayer.stop();
|
||||
} else {
|
||||
mVlcPlayer.stop();
|
||||
}
|
||||
@@ -227,8 +281,8 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
public long seekTo(long pos) {
|
||||
if (nativeMode) {
|
||||
Long intPos = pos;
|
||||
TvApp.getApplication().getLogger().Info("Exo length in seek is: " + mVideoView.getDuration());
|
||||
mVideoView.seekTo(intPos.intValue());
|
||||
TvApp.getApplication().getLogger().Info("Exo length in seek is: " + mExoPlayer.getDuration());
|
||||
mExoPlayer.seekTo(intPos.intValue());
|
||||
return pos;
|
||||
} else {
|
||||
if (mVlcPlayer == null || !mVlcPlayer.isSeekable()) return -1;
|
||||
@@ -259,7 +313,9 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
if (nativeMode) {
|
||||
try {
|
||||
mVideoView.setVideoPath(path);
|
||||
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(TvApp.getApplication(), "ATV/ExoPlayer");
|
||||
|
||||
mExoPlayer.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(path)));
|
||||
} catch (IllegalStateException e) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Unable to set video path. Probably backing out.", e);
|
||||
}
|
||||
@@ -277,7 +333,7 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public void hideSurface() {
|
||||
if (nativeMode) {
|
||||
mVideoView.setVisibility(View.INVISIBLE);
|
||||
mExoPlayerView.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mSurfaceView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
@@ -285,7 +341,7 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public void showSurface() {
|
||||
if (nativeMode) {
|
||||
mVideoView.setVisibility(View.VISIBLE);
|
||||
mExoPlayerView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mSurfaceView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@@ -431,12 +487,12 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
options.add("--network-caching=" + buffer);
|
||||
options.add("--no-audio-time-stretch");
|
||||
options.add("--avcodec-skiploopfilter");
|
||||
options.add("" + 1);
|
||||
options.add("1");
|
||||
options.add("--avcodec-skip-frame");
|
||||
options.add("0");
|
||||
options.add("--avcodec-skip-idct");
|
||||
options.add("0");
|
||||
options.add("--androidwindow-chroma");
|
||||
options.add("--android-display-chroma");
|
||||
options.add("RV32");
|
||||
options.add("--audio-resampler");
|
||||
options.add("soxr");
|
||||
@@ -449,18 +505,8 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
// options.add("Universal (UTF-8)");
|
||||
options.add("-v");
|
||||
|
||||
mLibVLC = new LibVLC(options);
|
||||
mLibVLC = new LibVLC(TvApp.getApplication(), options);
|
||||
TvApp.getApplication().getLogger().Info("Network buffer set to " + buffer);
|
||||
LibVLC.setOnNativeCrashListener(new LibVLC.OnNativeCrashListener() {
|
||||
@Override
|
||||
public void onNativeCrash() {
|
||||
new Exception().printStackTrace();
|
||||
//todo custom error reporter
|
||||
mActivity.finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(10);
|
||||
}
|
||||
});
|
||||
|
||||
mVlcPlayer = new org.videolan.libvlc.MediaPlayer(mLibVLC);
|
||||
mVlcPlayer.setAudioOutput(Utils.downMixAudio() ? "opensles_android" : "android_audiotrack");
|
||||
@@ -474,12 +520,9 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
mVlcPlayer.getVLCVout().detachViews();
|
||||
mVlcPlayer.getVLCVout().setVideoView(mSurfaceView);
|
||||
if (hasSubtitlesSurface) mVlcPlayer.getVLCVout().setSubtitlesView(mSubtitlesSurface);
|
||||
mVlcPlayer.getVLCVout().attachViews();
|
||||
mVlcPlayer.getVLCVout().attachViews(this);
|
||||
TvApp.getApplication().getLogger().Debug("Surface attached");
|
||||
mSurfaceReady = true;
|
||||
mVlcPlayer.getVLCVout().addCallback(this);
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
TvApp.getApplication().getLogger().ErrorException("Error creating VLC player", e);
|
||||
Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getString(R.string.msg_video_playback_error));
|
||||
@@ -487,23 +530,30 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
}
|
||||
|
||||
private void releasePlayer() {
|
||||
if (mVlcPlayer == null) return;
|
||||
if (mVlcPlayer != null) {
|
||||
mVlcPlayer.setEventListener(null);
|
||||
mVlcPlayer.stop();
|
||||
mVlcPlayer.getVLCVout().detachViews();
|
||||
mVlcPlayer.release();
|
||||
mLibVLC.release();
|
||||
mLibVLC = null;
|
||||
mVlcPlayer = null;
|
||||
}
|
||||
|
||||
if (mExoPlayer != null) {
|
||||
mExoPlayerView.setPlayer(null);
|
||||
mExoPlayer.release();
|
||||
mExoPlayer = null;
|
||||
}
|
||||
|
||||
mVlcPlayer.setEventListener(null);
|
||||
mVlcPlayer.stop();
|
||||
mVlcPlayer.getVLCVout().detachViews();
|
||||
mVlcPlayer.release();
|
||||
mLibVLC = null;
|
||||
mVlcPlayer = null;
|
||||
mSurfaceView.setKeepScreenOn(false);
|
||||
|
||||
}
|
||||
|
||||
int normalWidth;
|
||||
int normalHeight;
|
||||
|
||||
public void contractVideo(int height) {
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) (nativeMode ? mVideoView.getLayoutParams() : mSurfaceView.getLayoutParams());
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) (nativeMode ? mExoPlayerView.getLayoutParams() : mSurfaceView.getLayoutParams());
|
||||
if (isContracted) return;
|
||||
|
||||
Activity activity = TvApp.getApplication().getCurrentActivity();
|
||||
@@ -516,8 +566,8 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
lp.bottomMargin = ((lp.height - normalHeight) / 2) - 50;
|
||||
|
||||
if (nativeMode) {
|
||||
mVideoView.setLayoutParams(lp);
|
||||
mVideoView.invalidate();
|
||||
mExoPlayerView.setLayoutParams(lp);
|
||||
mExoPlayerView.invalidate();
|
||||
} else mSurfaceView.setLayoutParams(lp);
|
||||
|
||||
isContracted = true;
|
||||
@@ -526,14 +576,14 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
|
||||
public void setVideoFullSize() {
|
||||
if (normalHeight == 0) return;
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) (nativeMode ? mVideoView.getLayoutParams() : mSurfaceView.getLayoutParams());
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) (nativeMode ? mExoPlayerView.getLayoutParams() : mSurfaceView.getLayoutParams());
|
||||
lp.height = normalHeight;
|
||||
lp.width = normalWidth;
|
||||
if (nativeMode) {
|
||||
lp.rightMargin = 0;
|
||||
lp.bottomMargin = 0;
|
||||
mVideoView.setLayoutParams(lp);
|
||||
mVideoView.invalidate();
|
||||
mExoPlayerView.setLayoutParams(lp);
|
||||
mExoPlayerView.invalidate();
|
||||
} else mSurfaceView.setLayoutParams(lp);
|
||||
|
||||
isContracted = false;
|
||||
@@ -609,19 +659,7 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
if (hasSubtitlesSurface) mSubtitlesSurface.invalidate();
|
||||
}
|
||||
|
||||
PlaybackListener errorListener;
|
||||
|
||||
public void setOnErrorListener(final PlaybackListener listener) {
|
||||
mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
TvApp.getApplication().getLogger().Error("***** Got error from player");
|
||||
listener.onEvent();
|
||||
stopProgressLoop();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mVlcHandler.setOnErrorListener(listener);
|
||||
errorListener = listener;
|
||||
}
|
||||
@@ -631,28 +669,12 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
}
|
||||
|
||||
public void setOnCompletionListener(final PlaybackListener listener) {
|
||||
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
listener.onEvent();
|
||||
stopProgressLoop();
|
||||
}
|
||||
});
|
||||
|
||||
completionListener = listener;
|
||||
mVlcHandler.setOnCompletionListener(listener);
|
||||
}
|
||||
|
||||
private MediaPlayer mNativeMediaPlayer;
|
||||
|
||||
public void setOnPreparedListener(final PlaybackListener listener) {
|
||||
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
mNativeMediaPlayer = mp;
|
||||
listener.onEvent();
|
||||
startProgressLoop();
|
||||
}
|
||||
});
|
||||
preparedListener = listener;
|
||||
|
||||
mVlcHandler.setOnPreparedListener(listener);
|
||||
}
|
||||
@@ -701,7 +723,7 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onNewLayout(IVLCVout vout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
|
||||
public void onNewVideoLayout(IVLCVout vout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
|
||||
if (width * height == 0 || isContracted)
|
||||
return;
|
||||
|
||||
@@ -719,23 +741,6 @@ public class VideoManager implements IVLCVout.Callback {
|
||||
changeSurfaceLayout(mVideoWidth, mVideoHeight, mVideoVisibleWidth, mVideoVisibleHeight, mSarNum, mSarDen);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHardwareAccelerationError(IVLCVout ivlcVout) {
|
||||
TvApp.getApplication().getLogger().Error("VLC Hardware acceleration error");
|
||||
TvApp.getApplication().getPlaybackController().playerErrorEncountered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfacesCreated(IVLCVout ivlcVout) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfacesDestroyed(IVLCVout ivlcVout) {
|
||||
|
||||
}
|
||||
|
||||
public Integer translateVlcAudioId(Integer vlcId) {
|
||||
|
||||
@@ -3,10 +3,11 @@ package org.jellyfin.androidtv.presentation;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.support.v17.leanback.widget.BaseCardView;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v7.graphics.Palette;
|
||||
|
||||
import androidx.leanback.widget.BaseCardView;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -64,17 +65,17 @@ public class CardPresenter extends Presenter {
|
||||
|
||||
static class ViewHolder extends Presenter.ViewHolder {
|
||||
private int cardWidth = 230;
|
||||
|
||||
private int cardHeight = 280;
|
||||
|
||||
private BaseRowItem mItem;
|
||||
private MyImageCardView mCardView;
|
||||
private Drawable mDefaultCardImage;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
super(view);
|
||||
mCardView = (MyImageCardView) view;
|
||||
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.video);
|
||||
mCardView = (MyImageCardView) view;
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_video);
|
||||
}
|
||||
|
||||
public int getCardHeight() {
|
||||
@@ -93,95 +94,113 @@ public class CardPresenter extends Presenter {
|
||||
BaseItemDto itemDto = mItem.getBaseItem();
|
||||
boolean showWatched = true;
|
||||
boolean showProgress = false;
|
||||
double aspect = imageType.equals(ImageType.BANNER) ? ASPECT_RATIO_BANNER : imageType.equals(ImageType.THUMB) ? ImageUtils.ASPECT_RATIO_16_9 : Utils.getSafeValue(ImageUtils.getImageAspectRatio(itemDto, m.getPreferParentThumb()), ImageUtils.ASPECT_RATIO_7_9);
|
||||
switch (itemDto.getType()) {
|
||||
case "Audio":
|
||||
case "MusicAlbum":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.audio);
|
||||
if (aspect < 0.8) aspect = 1.0;
|
||||
double aspect;
|
||||
if (imageType.equals(ImageType.BANNER)) {
|
||||
aspect = ASPECT_RATIO_BANNER;
|
||||
} else if (imageType.equals(ImageType.THUMB)) {
|
||||
aspect = ImageUtils.ASPECT_RATIO_16_9;
|
||||
} else {
|
||||
aspect = Utils.getSafeValue(ImageUtils.getImageAspectRatio(itemDto, m.getPreferParentThumb()), ImageUtils.ASPECT_RATIO_7_9);
|
||||
}
|
||||
switch (itemDto.getBaseItemType()) {
|
||||
case Audio:
|
||||
case MusicAlbum:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_audio);
|
||||
if (aspect < 0.8) {
|
||||
aspect = 1.0;
|
||||
}
|
||||
showWatched = false;
|
||||
break;
|
||||
case "Person":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.person);
|
||||
case Person:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_person);
|
||||
break;
|
||||
case "MusicArtist":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.person);
|
||||
if (aspect <.8) aspect = 1.0;
|
||||
case MusicArtist:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_person);
|
||||
if (aspect < .8) {
|
||||
aspect = 1.0;
|
||||
}
|
||||
showWatched = false;
|
||||
break;
|
||||
case "RecordingGroup":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.recgroup);
|
||||
case RecordingGroup:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_record);
|
||||
break;
|
||||
case "Season":
|
||||
case "Series":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tv);
|
||||
case Season:
|
||||
case Series:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_tv);
|
||||
break;
|
||||
case "Episode":
|
||||
case Episode:
|
||||
//TvApp.getApplication().getLogger().Debug("**** Image width: "+ cardWidth + " Aspect: " + Utils.getImageAspectRatio(itemDto, m.getPreferParentThumb()) + " Item: "+itemDto.getName());
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tvl);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_land_tv);
|
||||
switch (itemDto.getLocationType()) {
|
||||
|
||||
case FileSystem:
|
||||
break;
|
||||
case Remote:
|
||||
break;
|
||||
case Virtual:
|
||||
mCardView.setBanner((itemDto.getPremiereDate() != null ? TimeUtils.convertToLocalDate(itemDto.getPremiereDate()) : new Date(System.currentTimeMillis()+1)).getTime() > System.currentTimeMillis() ? R.drawable.futurebanner : R.drawable.missingbanner);
|
||||
mCardView.setBanner((itemDto.getPremiereDate() != null ? TimeUtils.convertToLocalDate(itemDto.getPremiereDate()) : new Date(System.currentTimeMillis() + 1)).getTime() > System.currentTimeMillis() ? R.drawable.banner_edge_future : R.drawable.banner_edge_missing);
|
||||
break;
|
||||
case Offline:
|
||||
mCardView.setBanner(R.drawable.offlinebanner);
|
||||
mCardView.setBanner(R.drawable.banner_edge_offline);
|
||||
break;
|
||||
}
|
||||
showProgress = true;
|
||||
//Always show info for episodes
|
||||
mCardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
|
||||
break;
|
||||
case "CollectionFolder":
|
||||
case "Folder":
|
||||
case "MovieGenreFolder":
|
||||
case "MusicGenreFolder":
|
||||
case "MovieGenre":
|
||||
case "Genre":
|
||||
case "MusicGenre":
|
||||
case "UserView":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.folder);
|
||||
case CollectionFolder:
|
||||
// Force the aspect ratio to 16x9 because the server is returning the wrong value of 1
|
||||
aspect = ImageUtils.ASPECT_RATIO_16_9;
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_folder);
|
||||
break;
|
||||
case "Photo":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.photo);
|
||||
case Folder:
|
||||
case MovieGenreFolder:
|
||||
case MusicGenreFolder:
|
||||
case MovieGenre:
|
||||
case Genre:
|
||||
case MusicGenre:
|
||||
case UserView:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_folder);
|
||||
break;
|
||||
case Photo:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_land_photo);
|
||||
showWatched = false;
|
||||
break;
|
||||
case "PhotoAlbum":
|
||||
case "Playlist":
|
||||
case PhotoAlbum:
|
||||
case Playlist:
|
||||
showWatched = false;
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.folder);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_folder);
|
||||
break;
|
||||
case "Movie":
|
||||
case "Video":
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.video);
|
||||
case Movie:
|
||||
case Video:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_video);
|
||||
showProgress = true;
|
||||
break;
|
||||
default:
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.video);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_video);
|
||||
break;
|
||||
}
|
||||
cardHeight = !m.isStaticHeight() ? (aspect > 1 ? lHeight : pHeight) : sHeight;
|
||||
cardWidth = (int)(aspect * cardHeight);
|
||||
if (cardWidth < 10) cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
if (itemDto.getLocationType() == LocationType.Offline) mCardView.setBanner(R.drawable.offlinebanner);
|
||||
if (itemDto.getIsPlaceHolder() != null && itemDto.getIsPlaceHolder()) mCardView.setBanner(R.drawable.externaldiscbanner);
|
||||
cardWidth = (int) (aspect * cardHeight);
|
||||
if (cardWidth < 10) {
|
||||
cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
}
|
||||
if (itemDto.getLocationType() == LocationType.Offline) {
|
||||
mCardView.setBanner(R.drawable.banner_edge_offline);
|
||||
}
|
||||
if (itemDto.getIsPlaceHolder() != null && itemDto.getIsPlaceHolder()) {
|
||||
mCardView.setBanner(R.drawable.banner_edge_disc);
|
||||
}
|
||||
UserItemDataDto userData = itemDto.getUserData();
|
||||
if (showWatched && userData != null) {
|
||||
if (userData.getPlayed()) {
|
||||
mCardView.setUnwatchedCount(0);
|
||||
} else {
|
||||
if (userData.getUnplayedItemCount() != null) {
|
||||
mCardView.setUnwatchedCount(userData.getUnplayedItemCount());
|
||||
}
|
||||
} else if (userData.getUnplayedItemCount() != null) {
|
||||
mCardView.setUnwatchedCount(userData.getUnplayedItemCount());
|
||||
}
|
||||
}
|
||||
|
||||
if(showProgress && itemDto.getRunTimeTicks() != null && itemDto.getRunTimeTicks() > 0 && userData != null && userData.getPlaybackPositionTicks() > 0) {
|
||||
mCardView.setProgress(((int)(userData.getPlaybackPositionTicks() * 100.0 / itemDto.getRunTimeTicks()))); // force floating pt math with 100.0
|
||||
if (showProgress && itemDto.getRunTimeTicks() != null && itemDto.getRunTimeTicks() > 0 && userData != null && userData.getPlaybackPositionTicks() > 0) {
|
||||
mCardView.setProgress(((int) (userData.getPlaybackPositionTicks() * 100.0 / itemDto.getRunTimeTicks()))); // force floating pt math with 100.0
|
||||
} else {
|
||||
mCardView.setProgress(0);
|
||||
}
|
||||
@@ -191,103 +210,109 @@ public class CardPresenter extends Presenter {
|
||||
ChannelInfoDto channel = mItem.getChannelInfo();
|
||||
double tvAspect = imageType.equals(ImageType.BANNER) ? ASPECT_RATIO_BANNER : imageType.equals(ImageType.THUMB) ? ImageUtils.ASPECT_RATIO_16_9 : Utils.getSafeValue(channel.getPrimaryImageAspectRatio(), ImageUtils.ASPECT_RATIO_7_9);
|
||||
cardHeight = !m.isStaticHeight() ? tvAspect > 1 ? lHeight : pHeight : sHeight;
|
||||
cardWidth = (int)((tvAspect) * cardHeight);
|
||||
if (cardWidth < 10) cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
cardWidth = (int) ((tvAspect) * cardHeight);
|
||||
if (cardWidth < 10) {
|
||||
cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
}
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tv);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_tv);
|
||||
break;
|
||||
|
||||
case LiveTvProgram:
|
||||
BaseItemDto program = mItem.getProgramInfo();
|
||||
Double programAspect = program.getPrimaryImageAspectRatio();
|
||||
if (programAspect == null) programAspect = Utils.isTrue(program.getIsMovie()) ? ImageUtils.ASPECT_RATIO_2_3 : ImageUtils.ASPECT_RATIO_16_9;
|
||||
if (Utils.isTrue(program.getIsMovie())) {
|
||||
// The server reports the incorrect image aspect ratio for movies, so we are overriding it here
|
||||
programAspect = ImageUtils.ASPECT_RATIO_2_3;
|
||||
} else if (programAspect == null) {
|
||||
programAspect = ImageUtils.ASPECT_RATIO_16_9;
|
||||
}
|
||||
cardHeight = !m.isStaticHeight() ? programAspect > 1 ? lHeight : pHeight : sHeight;
|
||||
cardWidth = (int)((programAspect) * cardHeight);
|
||||
if (cardWidth < 10) cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
cardWidth = (int) ((programAspect) * cardHeight);
|
||||
if (cardWidth < 10) {
|
||||
cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
}
|
||||
switch (program.getLocationType()) {
|
||||
|
||||
case FileSystem:
|
||||
break;
|
||||
case Remote:
|
||||
case Offline:
|
||||
break;
|
||||
case Virtual:
|
||||
if (program.getStartDate() != null && TimeUtils.convertToLocalDate(program.getStartDate()).getTime() > System.currentTimeMillis()) mCardView.setBanner(R.drawable.futurebanner);
|
||||
break;
|
||||
case Offline:
|
||||
if (program.getStartDate() != null && TimeUtils.convertToLocalDate(program.getStartDate()).getTime() > System.currentTimeMillis()) {
|
||||
mCardView.setBanner(R.drawable.banner_edge_future);
|
||||
}
|
||||
break;
|
||||
}
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tvl);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_land_tv);
|
||||
//Always show info for programs
|
||||
mCardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
|
||||
break;
|
||||
|
||||
case LiveTvRecording:
|
||||
BaseItemDto recording = mItem.getRecordingInfo();
|
||||
double recordingAspect = imageType.equals(ImageType.BANNER) ? ASPECT_RATIO_BANNER : (imageType.equals(ImageType.THUMB) ? ImageUtils.ASPECT_RATIO_16_9 : Utils.getSafeValue(recording.getPrimaryImageAspectRatio(), ImageUtils.ASPECT_RATIO_7_9));
|
||||
cardHeight = !m.isStaticHeight() ? recordingAspect > 1 ? lHeight : pHeight : sHeight;
|
||||
cardWidth = (int)((recordingAspect) * cardHeight);
|
||||
if (cardWidth < 10) cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
cardWidth = (int) ((recordingAspect) * cardHeight);
|
||||
if (cardWidth < 10) {
|
||||
cardWidth = 230; //Guard against zero size images causing picasso to barf
|
||||
}
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tv);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_tv);
|
||||
break;
|
||||
|
||||
case Server:
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.server);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_server);
|
||||
break;
|
||||
case Person:
|
||||
cardHeight = !m.isStaticHeight() ? pHeight : sHeight;
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.person);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_person);
|
||||
break;
|
||||
case User:
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.person);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_person);
|
||||
break;
|
||||
case Chapter:
|
||||
cardHeight = !m.isStaticHeight() ? pHeight : sHeight;
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_16_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_16_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.chaptertile);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_chapter);
|
||||
break;
|
||||
case SearchHint:
|
||||
switch (mItem.getSearchHint().getType()) {
|
||||
case "Episode":
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_16_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_16_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tv);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_tv);
|
||||
break;
|
||||
case "Person":
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.person);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_person);
|
||||
break;
|
||||
default:
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.video);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_video);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GridButton:
|
||||
cardHeight = !m.isStaticHeight() ? pHeight : sHeight;
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_7_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.video);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_port_video);
|
||||
break;
|
||||
|
||||
case SeriesTimer:
|
||||
cardHeight = !m.isStaticHeight() ? pHeight : sHeight;
|
||||
cardWidth = (int)(ImageUtils.ASPECT_RATIO_16_9 * cardHeight);
|
||||
cardWidth = (int) (ImageUtils.ASPECT_RATIO_16_9 * cardHeight);
|
||||
mCardView.setMainImageDimensions(cardWidth, cardHeight);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.seriestimer);
|
||||
mDefaultCardImage = TvApp.getApplication().getDrawableCompat(R.drawable.tile_land_series_timer);
|
||||
//Always show info for timers
|
||||
mCardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,19 +329,18 @@ public class CardPresenter extends Presenter {
|
||||
}
|
||||
|
||||
protected void updateCardViewImage(String url) {
|
||||
if (!validContext()) return;
|
||||
if (!validContext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (url == null) {
|
||||
//TvApp.getApplication().getLogger().Debug("Clearing card image");
|
||||
Glide.with(getContext())
|
||||
.load("nothing")
|
||||
.fitCenter()
|
||||
.error(mDefaultCardImage)
|
||||
.into(mCardView.getMainImageView());
|
||||
|
||||
} else {
|
||||
//TvApp.getApplication().getLogger().Debug("Loading card image");
|
||||
Glide.with(getContext())
|
||||
.load(url)
|
||||
.asBitmap()
|
||||
@@ -330,7 +354,6 @@ public class CardPresenter extends Presenter {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
TvApp.getApplication().getLogger().Info("Image load aborted due to activity closing");
|
||||
}
|
||||
@@ -340,25 +363,23 @@ public class CardPresenter extends Presenter {
|
||||
mCardView.clearBanner();
|
||||
mCardView.setUnwatchedCount(-1);
|
||||
mCardView.setProgress(0);
|
||||
if (!validContext()) return;
|
||||
//TvApp.getApplication().getLogger().Debug("Resetting card image");
|
||||
if (!validContext()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Glide.with(getContext())
|
||||
.load(Uri.parse("android.resource://org.jellyfin.androidtv/drawable/loading"))
|
||||
.load(TvApp.getApplication().getDrawable(R.drawable.loading))
|
||||
.fitCenter()
|
||||
.error(mDefaultCardImage)
|
||||
.into(mCardView.getMainImageView());
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
TvApp.getApplication().getLogger().Info("Image reset aborted due to activity closing");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent) {
|
||||
//Log.d(TAG, "onCreateViewHolder");
|
||||
mViewParent = parent;
|
||||
|
||||
MyImageCardView cardView = new MyImageCardView(getContext(), mShowInfo);
|
||||
@@ -370,17 +391,22 @@ public class CardPresenter extends Presenter {
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
|
||||
if (!(item instanceof BaseRowItem)) return;
|
||||
if (!(item instanceof BaseRowItem)) {
|
||||
return;
|
||||
}
|
||||
BaseRowItem rowItem = (BaseRowItem) item;
|
||||
if (!rowItem.isValid()) return;
|
||||
|
||||
if (!rowItem.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewHolder holder = (ViewHolder) viewHolder;
|
||||
holder.setItem(rowItem, mImageType, 260, 300, mStaticHeight);
|
||||
|
||||
holder.mCardView.setTitleText(rowItem.getCardName());
|
||||
holder.mCardView.setContentText(rowItem.getSubText());
|
||||
if (ImageType.DEFAULT.equals(mImageType)) holder.mCardView.setOverlayInfo(rowItem);
|
||||
if (ImageType.DEFAULT.equals(mImageType)) {
|
||||
holder.mCardView.setOverlayInfo(rowItem);
|
||||
}
|
||||
holder.mCardView.showFavIcon(rowItem.isFavorite());
|
||||
if (rowItem.isPlaying()) {
|
||||
holder.mCardView.setPlayingIndicator(true);
|
||||
@@ -389,24 +415,19 @@ public class CardPresenter extends Presenter {
|
||||
Drawable badge = rowItem.getBadgeImage();
|
||||
if (badge != null) {
|
||||
((ViewHolder) viewHolder).mCardView.setBadgeImage(badge);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
((ViewHolder) viewHolder).updateCardViewImage(rowItem.getImageUrl(mImageType, ((ViewHolder) viewHolder).getCardHeight()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
|
||||
//TvApp.getApplication().getLogger().Debug("onUnbindViewHolder");
|
||||
//Get the image out of there so won't be there if recycled
|
||||
((ViewHolder) viewHolder).resetCardViewImage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(Presenter.ViewHolder viewHolder) {
|
||||
//Log.d(TAG, "onViewAttachedToWindow");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.ListRowPresenter;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -19,7 +19,9 @@ public class GridButtonPresenter extends Presenter {
|
||||
private int mCardHeight = 220;
|
||||
private static ViewGroup mViewParent;
|
||||
|
||||
public GridButtonPresenter() { super();}
|
||||
public GridButtonPresenter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public GridButtonPresenter(boolean showinfo, int width, int height) {
|
||||
this();
|
||||
@@ -41,9 +43,9 @@ public class GridButtonPresenter extends Presenter {
|
||||
|
||||
public ViewHolder(View view) {
|
||||
super(view);
|
||||
mCardView = (MyImageCardView) view;
|
||||
|
||||
mDefaultCardImage = TvApp.getApplication().getResources().getDrawable(R.drawable.gears);
|
||||
mCardView = (MyImageCardView) view;
|
||||
mDefaultCardImage = TvApp.getApplication().getResources().getDrawable(R.drawable.tile_settings);
|
||||
}
|
||||
|
||||
public void setItem(GridButton m, int width, int height) {
|
||||
@@ -73,7 +75,6 @@ public class GridButtonPresenter extends Presenter {
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent) {
|
||||
//Log.d(TAG, "onCreateViewHolder");
|
||||
mViewParent = parent;
|
||||
|
||||
MyImageCardView cardView = new MyImageCardView(getContext(), mShowInfo);
|
||||
@@ -91,22 +92,16 @@ public class GridButtonPresenter extends Presenter {
|
||||
ViewHolder vh = (ViewHolder) viewHolder;
|
||||
|
||||
vh.setItem(gridItem, mCardWidth, mCardHeight);
|
||||
|
||||
//Log.d(TAG, "onBindViewHolder");
|
||||
vh.mCardView.setTitleText(gridItem.getText());
|
||||
vh.mCardView.setOverlayText(gridItem.getText());
|
||||
vh.updateCardViewImage(gridItem.getImageIndex());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
|
||||
//Log.d(TAG, "onUnbindViewHolder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(Presenter.ViewHolder viewHolder) {
|
||||
//Log.d(TAG, "onViewAttachedToWindow");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,16 +4,18 @@ package org.jellyfin.androidtv.presentation;
|
||||
* Created by Eric on 8/17/2015.
|
||||
*/
|
||||
|
||||
import android.support.v17.leanback.widget.FocusHighlight;
|
||||
import android.support.v17.leanback.widget.FocusHighlightHelper;
|
||||
import android.support.v17.leanback.widget.HorizontalGridView;
|
||||
import android.support.v17.leanback.widget.ItemBridgeAdapter;
|
||||
import android.support.v17.leanback.widget.ObjectAdapter;
|
||||
import android.support.v17.leanback.widget.OnChildSelectedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.ShadowOverlayContainer;
|
||||
import androidx.leanback.widget.FocusHighlight;
|
||||
import androidx.leanback.widget.FocusHighlightHelper;
|
||||
import androidx.leanback.widget.HorizontalGridView;
|
||||
import androidx.leanback.widget.ItemBridgeAdapter;
|
||||
import androidx.leanback.widget.ObjectAdapter;
|
||||
import androidx.leanback.widget.OnChildViewHolderSelectedListener;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.ShadowOverlayContainer;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -187,7 +189,7 @@ public class HorizontalGridPresenter extends Presenter {
|
||||
vh.getGridView().setNumRows(mNumRows);
|
||||
vh.getGridView().setFadingRightEdge(true);
|
||||
vh.getGridView().setFadingRightEdgeLength(100);
|
||||
vh.getGridView().setItemMargin(10);
|
||||
vh.getGridView().setItemSpacing(10);
|
||||
vh.mInitialized = true;
|
||||
|
||||
vh.mItemBridgeAdapter.setWrapper(mWrapper);
|
||||
@@ -200,10 +202,10 @@ public class HorizontalGridPresenter extends Presenter {
|
||||
mZoomFactor, true);
|
||||
|
||||
final ViewHolder gridViewHolder = vh;
|
||||
vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() {
|
||||
vh.getGridView().setOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
|
||||
@Override
|
||||
public void onChildSelected(ViewGroup parent, View view, int position, long id) {
|
||||
selectChildView(gridViewHolder, view);
|
||||
public void onChildViewHolderSelected(RecyclerView parent, RecyclerView.ViewHolder child, int position, int subposition) {
|
||||
selectChildView(gridViewHolder, child.itemView);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Paint;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -36,8 +36,8 @@ public abstract class MyAbstractDetailsPresenter extends Presenter {
|
||||
|
||||
public ViewHolder(View view) {
|
||||
super(view);
|
||||
mTitle = (TextView) view.findViewById(android.support.v17.leanback.R.id.lb_details_description_title);
|
||||
mSubtitle = (TextView) view.findViewById(android.support.v17.leanback.R.id.lb_details_description_subtitle);
|
||||
mTitle = (TextView) view.findViewById(androidx.leanback.R.id.lb_details_description_title);
|
||||
mSubtitle = (TextView) view.findViewById(androidx.leanback.R.id.lb_details_description_subtitle);
|
||||
mActivity = (Activity) view.getContext();
|
||||
mInfoRow = new LinearLayout(mActivity);
|
||||
LinearLayout.LayoutParams layout = new LinearLayout.LayoutParams(Utils.convertDpToPixel(mActivity, 500), Utils.convertDpToPixel(mActivity,20));
|
||||
@@ -48,23 +48,23 @@ public abstract class MyAbstractDetailsPresenter extends Presenter {
|
||||
int index = parent.indexOfChild(mSubtitle);
|
||||
parent.removeView(mSubtitle);
|
||||
parent.addView(mInfoRow, index);
|
||||
mBody = (TextView) view.findViewById(android.support.v17.leanback.R.id.lb_details_description_body);
|
||||
mBody = (TextView) view.findViewById(androidx.leanback.R.id.lb_details_description_body);
|
||||
|
||||
Paint.FontMetricsInt titleFontMetricsInt = getFontMetricsInt(mTitle);
|
||||
final int titleAscent = view.getResources().getDimensionPixelSize(
|
||||
android.support.v17.leanback.R.dimen.lb_details_description_title_baseline);
|
||||
androidx.leanback.R.dimen.lb_details_description_title_baseline);
|
||||
// Ascent is negative
|
||||
mTitleMargin = titleAscent + titleFontMetricsInt.ascent;
|
||||
|
||||
mUnderTitleBaselineMargin = view.getResources().getDimensionPixelSize(
|
||||
android.support.v17.leanback.R.dimen.lb_details_description_under_title_baseline_margin);
|
||||
androidx.leanback.R.dimen.lb_details_description_under_title_baseline_margin);
|
||||
mUnderSubtitleBaselineMargin = view.getResources().getDimensionPixelSize(
|
||||
android.support.v17.leanback.R.dimen.lb_details_description_under_subtitle_baseline_margin);
|
||||
androidx.leanback.R.dimen.lb_details_description_under_subtitle_baseline_margin);
|
||||
|
||||
mTitleLineSpacing = view.getResources().getDimensionPixelSize(
|
||||
android.support.v17.leanback.R.dimen.lb_details_description_title_line_spacing);
|
||||
androidx.leanback.R.dimen.lb_details_description_title_line_spacing);
|
||||
mBodyLineSpacing = view.getResources().getDimensionPixelSize(
|
||||
android.support.v17.leanback.R.dimen.lb_details_description_body_line_spacing);
|
||||
androidx.leanback.R.dimen.lb_details_description_body_line_spacing);
|
||||
|
||||
mBodyMaxLines = 6;
|
||||
mBodyMinLines = 4;
|
||||
@@ -109,7 +109,7 @@ public abstract class MyAbstractDetailsPresenter extends Presenter {
|
||||
@Override
|
||||
public final ViewHolder onCreateViewHolder(ViewGroup parent) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(android.support.v17.leanback.R.layout.lb_details_description, parent, false);
|
||||
.inflate(androidx.leanback.R.layout.lb_details_description, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -112,10 +116,23 @@ public class MyDetailsOverviewRowPresenter extends RowPresenter {
|
||||
|
||||
vh.mPoster.setImageDrawable(row.getImageDrawable());
|
||||
//vh.mStudioImage.setImageDrawable(row.getStudioDrawable());
|
||||
vh.mSummary.setText(row.getSummary());
|
||||
//vh.mSummaryTitle.setText(row.getSummaryTitle());
|
||||
switch (row.getItem().getType()) {
|
||||
case "Person":
|
||||
|
||||
// Support simple HTML elements
|
||||
String summaryRaw = row.getSummary();
|
||||
if (summaryRaw != null) {
|
||||
Spanned summarySpanned;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
summarySpanned = Html.fromHtml(summaryRaw, Html.FROM_HTML_MODE_COMPACT);
|
||||
} else {
|
||||
summarySpanned = Html.fromHtml(summaryRaw);
|
||||
}
|
||||
|
||||
vh.mSummary.setText(summarySpanned);
|
||||
}
|
||||
|
||||
switch (row.getItem().getBaseItemType()) {
|
||||
case Person:
|
||||
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) vh.mSummary.getLayoutParams();
|
||||
params.topMargin = 10;
|
||||
params.height = Utils.convertDpToPixel(TvApp.getApplication(), 185);
|
||||
@@ -143,7 +160,7 @@ public class MyDetailsOverviewRowPresenter extends RowPresenter {
|
||||
for (String genre : item.getGenres()) {
|
||||
if (!first) InfoLayoutHelper.addSpacer(TvApp.getApplication().getCurrentActivity(), layout, " / ", 12);
|
||||
first = false;
|
||||
layout.addView(new GenreButton(TvApp.getApplication().getCurrentActivity(), TvApp.getApplication().getDefaultFont(), 14, genre, item.getType()));
|
||||
layout.addView(new GenreButton(TvApp.getApplication().getCurrentActivity(), TvApp.getApplication().getDefaultFont(), 14, genre, item.getBaseItemType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v17.leanback.widget.BaseCardView;
|
||||
import androidx.leanback.widget.BaseCardView;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -126,9 +125,9 @@ public class MyImageCardView extends BaseCardView {
|
||||
|
||||
public void setPlayingIndicator(boolean playing) {
|
||||
if (playing) {
|
||||
mBadgeImage.setBackgroundResource(R.drawable.eq_animation);
|
||||
// TODO use decent animation for equalizer icon
|
||||
mBadgeImage.setBackgroundResource(R.drawable.ic_play);
|
||||
mBadgeImage.setVisibility(VISIBLE);
|
||||
((AnimationDrawable)mBadgeImage.getBackground()).start();
|
||||
} else {
|
||||
mBadgeImage.setBackgroundResource(R.drawable.blank10x10);
|
||||
}
|
||||
@@ -238,28 +237,28 @@ public class MyImageCardView extends BaseCardView {
|
||||
if (mOverlayName == null) return;
|
||||
|
||||
if (getCardType() == BaseCardView.CARD_TYPE_MAIN_ONLY && item.showCardInfoOverlay()) {
|
||||
switch (item.getType()) {
|
||||
case "Photo":
|
||||
switch (item.getBaseItemType()) {
|
||||
case Photo:
|
||||
mOverlayName.setText(item.getBaseItem().getPremiereDate() != null ? android.text.format.DateFormat.getDateFormat(TvApp.getApplication()).format(TimeUtils.convertToLocalDate(item.getBaseItem().getPremiereDate())) : item.getFullName());
|
||||
mOverlayIcon.setImageResource(R.drawable.camera);
|
||||
mOverlayIcon.setImageResource(R.drawable.ic_camera);
|
||||
break;
|
||||
case "PhotoAlbum":
|
||||
case PhotoAlbum:
|
||||
mOverlayName.setText(item.getFullName());
|
||||
mOverlayIcon.setImageResource(R.drawable.photoalbum);
|
||||
mOverlayIcon.setImageResource(R.drawable.ic_photos);
|
||||
break;
|
||||
case "Video":
|
||||
case Video:
|
||||
mOverlayName.setText(item.getFullName());
|
||||
mOverlayIcon.setImageResource(R.drawable.film);
|
||||
mOverlayIcon.setImageResource(R.drawable.ic_movie);
|
||||
break;
|
||||
case "Playlist":
|
||||
case "MusicArtist":
|
||||
case "Person":
|
||||
case Playlist:
|
||||
case MusicArtist:
|
||||
case Person:
|
||||
mOverlayName.setText(item.getFullName());
|
||||
hideIcon();
|
||||
break;
|
||||
default:
|
||||
mOverlayName.setText(item.getFullName());
|
||||
mOverlayIcon.setImageResource(item.isFolder() ? R.drawable.foldersmall : R.drawable.blank30x30);
|
||||
mOverlayIcon.setImageResource(item.isFolder() ? R.drawable.ic_folder : R.drawable.blank30x30);
|
||||
break;
|
||||
}
|
||||
mOverlayCount.setText(item.getChildCountStr());
|
||||
@@ -389,6 +388,4 @@ public class MyImageCardView extends BaseCardView {
|
||||
mImageView.setAlpha(1f);
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import androidx.leanback.app.BackgroundManager;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.ListRowPresenter;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -1,88 +1,90 @@
|
||||
package org.jellyfin.androidtv.presentation;
|
||||
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Handler;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.base.BaseActivity;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
/**
|
||||
* Created by Eric on 10/24/2015.
|
||||
*/
|
||||
public class ThemeManager {
|
||||
|
||||
private static boolean isEnabled() {
|
||||
return TvApp.getApplication().getPrefs().getBoolean("pref_enable_themes",true);
|
||||
return TvApp.getApplication().getPrefs().getBoolean("pref_enable_themes", true);
|
||||
}
|
||||
|
||||
private static boolean isHalloween() {
|
||||
if (!isEnabled()) return false;
|
||||
if (!isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Calendar today = new GregorianCalendar();
|
||||
return today.get(Calendar.MONTH) == 9 && (today.get(Calendar.DAY_OF_MONTH) == 30 || today.get(Calendar.DAY_OF_MONTH) == 31);
|
||||
return today.get(Calendar.MONTH) == Calendar.OCTOBER &&
|
||||
(today.get(Calendar.DAY_OF_MONTH) == 30 || today.get(Calendar.DAY_OF_MONTH) == 31);
|
||||
}
|
||||
|
||||
private static boolean isHolidays() {
|
||||
if (!isEnabled()) return false;
|
||||
if (!isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Calendar today = new GregorianCalendar();
|
||||
return today.get(Calendar.MONTH) == 11 && (today.get(Calendar.DAY_OF_MONTH) > 20 && today.get(Calendar.DAY_OF_MONTH) < 27);
|
||||
return today.get(Calendar.MONTH) == Calendar.DECEMBER &&
|
||||
(today.get(Calendar.DAY_OF_MONTH) > 20 && today.get(Calendar.DAY_OF_MONTH) < 27);
|
||||
}
|
||||
|
||||
public static int getBrandColor() {
|
||||
return isHalloween() ?
|
||||
TvApp.getApplication().getResources().getColor(R.color.halloween_end) :
|
||||
isHolidays() ?
|
||||
TvApp.getApplication().getResources().getColor(R.color.holiday_end) :
|
||||
Utils.getBrandColor();
|
||||
private static void showHolidayMessage(final BaseActivity activity, final String title, final String message, final Integer icon, final Integer background) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// make sure they haven't moved away from that activity
|
||||
if (TvApp.getApplication().getCurrentActivity() == null ||
|
||||
TvApp.getApplication().getCurrentActivity() != activity) {
|
||||
return;
|
||||
}
|
||||
activity.showMessage(title, message, 10000, icon, background);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
public static void showWelcomeMessage() {
|
||||
final BaseActivity currentActivity = TvApp.getApplication().getCurrentActivity();
|
||||
if (isHalloween() && currentActivity != null) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// make sure they haven't moved away from that activity
|
||||
if (TvApp.getApplication().getCurrentActivity() == null || TvApp.getApplication().getCurrentActivity() != currentActivity) return;
|
||||
MediaPlayer mp = MediaPlayer.create(TvApp.getApplication(), R.raw.howl);
|
||||
mp.start();
|
||||
currentActivity.showMessage("Happy Halloween!", "Try some of our spooky suggestions", 10000, R.drawable.ghost, R.drawable.orange_gradient);
|
||||
}
|
||||
}, 2000);
|
||||
} else
|
||||
if (isHolidays() && currentActivity != null) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// make sure they haven't moved away from that activity
|
||||
if (TvApp.getApplication().getCurrentActivity() == null || TvApp.getApplication().getCurrentActivity() != currentActivity) return;
|
||||
MediaPlayer mp = MediaPlayer.create(TvApp.getApplication(), R.raw.sleighbells);
|
||||
mp.start();
|
||||
currentActivity.showMessage("Happy Holidays!", "Try some of our holiday suggestions", 10000, R.drawable.snowflake, R.drawable.holiday_gradient);
|
||||
}
|
||||
}, 2000);
|
||||
if (currentActivity != null) {
|
||||
if (isHalloween()) {
|
||||
showHolidayMessage(currentActivity,
|
||||
currentActivity.getString(R.string.title_suggestions_halloween),
|
||||
currentActivity.getString(R.string.desc_suggestions_halloween),
|
||||
R.drawable.ic_ghost,
|
||||
R.drawable.orange_gradient);
|
||||
} else if (isHolidays()) {
|
||||
showHolidayMessage(currentActivity,
|
||||
currentActivity.getString(R.string.title_suggestions_holiday),
|
||||
currentActivity.getString(R.string.desc_suggestions_holiday),
|
||||
R.drawable.ic_snowflake,
|
||||
R.drawable.holiday_gradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getSpecialGenres() {
|
||||
if (isHalloween()) {
|
||||
return new String[] {"Halloween","Horror"};
|
||||
return new String[]{"Halloween", "Horror"};
|
||||
}
|
||||
|
||||
if (isHolidays()) {
|
||||
return new String[] {"Holiday","Holidays","Christmas"};
|
||||
return new String[]{"Holiday", "Holidays", "Christmas"};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getSuggestionTitle() {
|
||||
return isHalloween() ? "Spooky Suggestions" : isHolidays() ? "Holiday Suggestions" : "Suggestions";
|
||||
int labelId = R.string.lbl_suggestions;
|
||||
if (isHalloween()) {
|
||||
labelId = R.string.lbl_suggestions_halloween;
|
||||
} else if (isHolidays()) {
|
||||
labelId = R.string.lbl_suggestions_holiday;
|
||||
}
|
||||
return TvApp.getApplication().getCurrentActivity().getString(labelId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,20 @@ public enum QueryType {
|
||||
StaticChapters,
|
||||
StaticServers,
|
||||
Users,
|
||||
Search, Specials, Trailers, LiveTvChannel, LiveTvProgram, LiveTvRecording, LiveTvRecordingGroup, StaticItems, Persons, StaticAudioQueueItems, AlbumArtists, AudioPlaylists, ContinueWatching, LatestItems, SeriesTimer, Premieres
|
||||
Search,
|
||||
Specials,
|
||||
Trailers,
|
||||
LiveTvChannel,
|
||||
LiveTvProgram,
|
||||
LiveTvRecording,
|
||||
LiveTvRecordingGroup,
|
||||
StaticItems,
|
||||
Persons,
|
||||
StaticAudioQueueItems,
|
||||
AlbumArtists,
|
||||
AudioPlaylists,
|
||||
ContinueWatching,
|
||||
LatestItems,
|
||||
SeriesTimer,
|
||||
Premieres
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.jellyfin.androidtv.search;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.jellyfin.androidtv.itemhandling.BaseRowItem;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemLauncher;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
|
||||
import androidx.leanback.app.SearchSupportFragment;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
|
||||
public class LeanbackSearchFragment extends SearchSupportFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Create provider
|
||||
SearchProvider searchProvider = new SearchProvider(getContext(), getActivity().getIntent().getBooleanExtra("MusicOnly", false));
|
||||
setSearchResultProvider(searchProvider);
|
||||
|
||||
// Create click listener
|
||||
setOnItemViewClickedListener((itemViewHolder, item, rowViewHolder, row) -> {
|
||||
if (!(item instanceof BaseRowItem)) return;
|
||||
|
||||
ItemLauncher.launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow) row).getAdapter(), ((BaseRowItem) item).getIndex(), getActivity());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
package org.jellyfin.androidtv.search;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.BackgroundManager;
|
||||
import android.support.v17.leanback.app.SearchFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||
import android.support.v17.leanback.widget.ObjectAdapter;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.squareup.picasso.Target;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.imagehandling.PicassoBackgroundManagerTarget;
|
||||
import org.jellyfin.androidtv.itemhandling.BaseRowItem;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemLauncher;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import java.util.Timer;
|
||||
|
||||
/**
|
||||
* Created by Eric on 1/26/2015.
|
||||
*/
|
||||
public class MySearchFragment extends SearchFragment
|
||||
implements SearchFragment.SearchResultProvider {
|
||||
|
||||
private static final int SEARCH_DELAY_MS = 1500;
|
||||
private final Handler mHandler = new Handler();
|
||||
private ArrayObjectAdapter mRowsAdapter;
|
||||
private SearchRunnable mDelayedLoad;
|
||||
|
||||
private Drawable mDefaultBackground;
|
||||
private Target mBackgroundTarget;
|
||||
private DisplayMetrics mMetrics;
|
||||
private Timer mBackgroundTimer;
|
||||
private String mBackgroundUrl;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
||||
setSearchResultProvider(this);
|
||||
setOnItemViewClickedListener(new OnItemViewClickedListener() {
|
||||
@Override
|
||||
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||
if (!(item instanceof BaseRowItem)) return;
|
||||
ItemLauncher.launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow)row).getAdapter(),((BaseRowItem) item).getIndex(), getActivity());
|
||||
}
|
||||
});
|
||||
mDelayedLoad = new SearchRunnable(getActivity(), mRowsAdapter, getActivity().getIntent().getBooleanExtra("MusicOnly",false));
|
||||
|
||||
prepareBackgroundManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectAdapter getResultsAdapter() {
|
||||
return mRowsAdapter;
|
||||
}
|
||||
|
||||
private boolean textEdited = false;
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newQuery) {
|
||||
textEdited = true;
|
||||
if (Utils.isNonEmpty(newQuery)) {
|
||||
mHandler.removeCallbacks(mDelayedLoad);
|
||||
mDelayedLoad.setQueryString(newQuery);
|
||||
mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
|
||||
} else {
|
||||
mRowsAdapter.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
if (!textEdited) {
|
||||
if (Utils.isNonEmpty(query)) {
|
||||
mHandler.removeCallbacks(mDelayedLoad);
|
||||
mDelayedLoad.setQueryString(query);
|
||||
mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
|
||||
} else {
|
||||
mRowsAdapter.clear();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void prepareBackgroundManager() {
|
||||
|
||||
BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
|
||||
backgroundManager.attach(getActivity().getWindow());
|
||||
mBackgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
|
||||
|
||||
mDefaultBackground = null;
|
||||
|
||||
mMetrics = new DisplayMetrics();
|
||||
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.jellyfin.androidtv.search;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v17.leanback.app.RowsFragment;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.itemhandling.BaseRowItem;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemLauncher;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
|
||||
/**
|
||||
* Created by Eric on 4/8/2015.
|
||||
*/
|
||||
public class MySearchFragmentNoSpeech extends Fragment {
|
||||
|
||||
private static final int SEARCH_DELAY_MS = 1500;
|
||||
|
||||
private RowsFragment mRowsFragment;
|
||||
private EditText mSearchField;
|
||||
private ArrayObjectAdapter mRowsAdapter;
|
||||
private SearchRunnable mDelayedLoad;
|
||||
private Handler mHandler = new Handler();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
View root = inflater.inflate(R.layout.fragment_empty_frame, container, false);
|
||||
|
||||
// Inject the RowsFragment in the results container
|
||||
if (getChildFragmentManager().findFragmentById(R.id.emptyFrame) == null) {
|
||||
mRowsFragment = new RowsFragment();
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.emptyFrame, mRowsFragment).commit();
|
||||
} else {
|
||||
mRowsFragment = (RowsFragment) getChildFragmentManager()
|
||||
.findFragmentById(R.id.emptyFrame);
|
||||
}
|
||||
|
||||
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
||||
mRowsFragment.setAdapter(mRowsAdapter);
|
||||
|
||||
// Setup item selection
|
||||
mRowsFragment.setOnItemViewClickedListener(new OnItemViewClickedListener() {
|
||||
@Override
|
||||
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||
if (!(item instanceof BaseRowItem)) return;
|
||||
ItemLauncher.launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow)row).getAdapter(), ((BaseRowItem) item).getIndex(), getActivity());
|
||||
}
|
||||
});
|
||||
|
||||
mDelayedLoad = new SearchRunnable(getActivity(), mRowsAdapter, getActivity().getIntent().getBooleanExtra("MusicOnly",false));
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// Setup search field listening
|
||||
mSearchField = (EditText) getActivity().findViewById(R.id.searchField);
|
||||
mSearchField.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (s.length() > 0) {
|
||||
mHandler.removeCallbacks(mDelayedLoad);
|
||||
mDelayedLoad.setQueryString(s.toString());
|
||||
mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
|
||||
|
||||
} else {
|
||||
mRowsAdapter.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,26 +3,34 @@ package org.jellyfin.androidtv.search;
|
||||
import android.os.Bundle;
|
||||
import android.speech.SpeechRecognizer;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.base.BaseActivity;
|
||||
|
||||
public class SearchActivity extends BaseActivity {
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class SearchActivity extends BaseActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (SpeechRecognizer.isRecognitionAvailable(TvApp.getApplication())) {
|
||||
setContentView(R.layout.activity_search);
|
||||
} else {
|
||||
setContentView(R.layout.activity_search_no_speech);
|
||||
}
|
||||
|
||||
boolean isSpeechEnabled = SpeechRecognizer.isRecognitionAvailable(this);
|
||||
|
||||
// Determine fragment to use
|
||||
Fragment searchFragment = isSpeechEnabled
|
||||
? new LeanbackSearchFragment()
|
||||
: new TextSearchFragment();
|
||||
|
||||
// Add fragment
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(android.R.id.content, searchFragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSearchRequested() {
|
||||
//re-start us
|
||||
this.recreate();
|
||||
// Reset layout
|
||||
recreate();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.jellyfin.androidtv.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
|
||||
import androidx.leanback.app.SearchSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.ListRowPresenter;
|
||||
import androidx.leanback.widget.ObjectAdapter;
|
||||
|
||||
public class SearchProvider implements SearchSupportFragment.SearchResultProvider {
|
||||
|
||||
private static final int SEARCH_DELAY_MS = 1500;
|
||||
private final Handler mHandler = new Handler();
|
||||
private ArrayObjectAdapter mRowsAdapter;
|
||||
private SearchRunnable mDelayedLoad;
|
||||
|
||||
SearchProvider(Context context, boolean musicOnly) {
|
||||
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
||||
mDelayedLoad = new SearchRunnable(context, mRowsAdapter, musicOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectAdapter getResultsAdapter() {
|
||||
return mRowsAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String query) {
|
||||
search(query, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
search(query, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update search results
|
||||
*
|
||||
* @param query String to search for
|
||||
* @param delayed When true the search is delayed by [SEARCH_DELAY_MS] milliseconds
|
||||
*/
|
||||
private void search(String query, boolean delayed) {
|
||||
// Clear results when query is empty
|
||||
if (Utils.isEmpty(query)) {
|
||||
mRowsAdapter.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove current delayed search (if any)
|
||||
mHandler.removeCallbacks(mDelayedLoad);
|
||||
|
||||
// Update search string
|
||||
mDelayedLoad.setQueryString(query);
|
||||
|
||||
// Schedule search depending on [delayed]
|
||||
if (delayed) mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
|
||||
else mHandler.post(mDelayedLoad);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,128 @@
|
||||
package org.jellyfin.androidtv.search;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.content.Context;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
import org.jellyfin.androidtv.presentation.CardPresenter;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
import org.jellyfin.apiclient.model.search.SearchQuery;
|
||||
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by Eric on 4/8/2015.
|
||||
*/
|
||||
public class SearchRunnable implements Runnable {
|
||||
private String searchString;
|
||||
private Activity activity;
|
||||
private Context context;
|
||||
private ArrayObjectAdapter mRowsAdapter;
|
||||
private boolean musicOnly;
|
||||
private int searchesReceived;
|
||||
private ArrayList<ItemRowAdapter> searchItemRows;
|
||||
|
||||
public void setQueryString(String value) {
|
||||
searchString = value;
|
||||
}
|
||||
|
||||
public SearchRunnable(Activity activity, ArrayObjectAdapter adapter, boolean musicOnly) {
|
||||
this.activity = activity;
|
||||
public SearchRunnable(Context context, ArrayObjectAdapter adapter, boolean musicOnly) {
|
||||
this.context = context;
|
||||
this.mRowsAdapter = adapter;
|
||||
this.musicOnly = musicOnly;
|
||||
this.searchItemRows = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mRowsAdapter.clear();
|
||||
searchItemRows.clear();
|
||||
searchesReceived = 0;
|
||||
|
||||
if (!musicOnly) {
|
||||
//Get search results by type
|
||||
SearchQuery people = getSearchQuery(new String[] {"Person","People"});
|
||||
ItemRowAdapter peopleAdapter = new ItemRowAdapter(people, new CardPresenter(), mRowsAdapter);
|
||||
ListRow peopleRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_people)), peopleAdapter);
|
||||
peopleAdapter.setRow(peopleRow);
|
||||
peopleAdapter.Retrieve();
|
||||
|
||||
SearchQuery movies = getSearchQuery(new String[] {"Movie", "BoxSet"});
|
||||
ItemRowAdapter movieAdapter = new ItemRowAdapter(movies, new CardPresenter(), mRowsAdapter);
|
||||
ListRow movieRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_movies)), movieAdapter);
|
||||
ListRow movieRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_movies)), movieAdapter);
|
||||
movieAdapter.setRow(movieRow);
|
||||
movieAdapter.Retrieve();
|
||||
retrieveSearchResult(movieAdapter);
|
||||
|
||||
SearchQuery tv = getSearchQuery(new String[] {"Series","Episode"});
|
||||
SearchQuery tvSeries = getSearchQuery(new String[] {"Series"});
|
||||
ItemRowAdapter tvSeriesAdapter = new ItemRowAdapter(tvSeries, new CardPresenter(), mRowsAdapter);
|
||||
ListRow tvSeriesRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_series)), tvSeriesAdapter);
|
||||
tvSeriesAdapter.setRow(tvSeriesRow);
|
||||
retrieveSearchResult(tvSeriesAdapter);
|
||||
|
||||
SearchQuery tv = getSearchQuery(new String[] {"Episode"});
|
||||
ItemRowAdapter tvAdapter = new ItemRowAdapter(tv, new CardPresenter(), mRowsAdapter);
|
||||
ListRow tvRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_tv)), tvAdapter);
|
||||
ListRow tvRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_episodes)), tvAdapter);
|
||||
tvAdapter.setRow(tvRow);
|
||||
tvAdapter.Retrieve();
|
||||
retrieveSearchResult(tvAdapter);
|
||||
|
||||
SearchQuery people = getSearchQuery(new String[] {"Person","People"});
|
||||
ItemRowAdapter peopleAdapter = new ItemRowAdapter(people, new CardPresenter(), mRowsAdapter);
|
||||
ListRow peopleRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_people)), peopleAdapter);
|
||||
peopleAdapter.setRow(peopleRow);
|
||||
retrieveSearchResult(peopleAdapter);
|
||||
|
||||
SearchQuery videos = getSearchQuery(new String[] {"Video"});
|
||||
ItemRowAdapter videoAdapter = new ItemRowAdapter(videos, new CardPresenter(), mRowsAdapter);
|
||||
ListRow videoRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_videos)), videoAdapter);
|
||||
ListRow videoRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_videos)), videoAdapter);
|
||||
videoAdapter.setRow(videoRow);
|
||||
videoAdapter.Retrieve();
|
||||
retrieveSearchResult(videoAdapter);
|
||||
|
||||
SearchQuery recordings = getSearchQuery(new String[] {"Recording"});
|
||||
ItemRowAdapter recordingAdapter = new ItemRowAdapter(recordings, new CardPresenter(), mRowsAdapter);
|
||||
ListRow recordingRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_recordings)), recordingAdapter);
|
||||
ListRow recordingRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_recordings)), recordingAdapter);
|
||||
recordingAdapter.setRow(recordingRow);
|
||||
recordingAdapter.Retrieve();
|
||||
retrieveSearchResult(recordingAdapter);
|
||||
|
||||
SearchQuery programs = getSearchQuery(new String[] {"Program"});
|
||||
ItemRowAdapter programAdapter = new ItemRowAdapter(programs, new CardPresenter(), mRowsAdapter);
|
||||
ListRow programRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_programs)), programAdapter);
|
||||
ListRow programRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_programs)), programAdapter);
|
||||
programAdapter.setRow(programRow);
|
||||
programAdapter.Retrieve();
|
||||
retrieveSearchResult(programAdapter);
|
||||
}
|
||||
|
||||
SearchQuery artists = getSearchQuery(new String[] {"MusicArtist"});
|
||||
ItemRowAdapter artistAdapter = new ItemRowAdapter(artists, new CardPresenter(), mRowsAdapter);
|
||||
ListRow artistRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_artists)), artistAdapter);
|
||||
ListRow artistRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_artists)), artistAdapter);
|
||||
artistAdapter.setRow(artistRow);
|
||||
artistAdapter.Retrieve();
|
||||
retrieveSearchResult(artistAdapter);
|
||||
|
||||
SearchQuery albums = getSearchQuery(new String[] {"MusicAlbum"});
|
||||
ItemRowAdapter albumAdapter = new ItemRowAdapter(albums, new CardPresenter(), mRowsAdapter);
|
||||
ListRow albumRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_albums)), albumAdapter);
|
||||
ListRow albumRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_albums)), albumAdapter);
|
||||
albumAdapter.setRow(albumRow);
|
||||
albumAdapter.Retrieve();
|
||||
retrieveSearchResult(albumAdapter);
|
||||
|
||||
SearchQuery songs = getSearchQuery(new String[] {"Audio"});
|
||||
ItemRowAdapter songAdapter = new ItemRowAdapter(songs, new CardPresenter(), mRowsAdapter);
|
||||
ListRow songRow = new ListRow(new HeaderItem(activity.getString(R.string.lbl_songs)), songAdapter);
|
||||
ListRow songRow = new ListRow(new HeaderItem(context.getString(R.string.lbl_songs)), songAdapter);
|
||||
songAdapter.setRow(songRow);
|
||||
songAdapter.Retrieve();
|
||||
retrieveSearchResult(songAdapter);
|
||||
|
||||
}
|
||||
|
||||
private void retrieveSearchResult(ItemRowAdapter itemRow) {
|
||||
searchItemRows.add(itemRow);
|
||||
itemRow.setRetrieveFinishedListener(new EmptyResponse() {
|
||||
@Override
|
||||
public void onResponse() {
|
||||
searchesReceived++;
|
||||
if (searchesReceived == searchItemRows.size()){
|
||||
for (ItemRowAdapter itemRowAdapter : searchItemRows) {
|
||||
itemRowAdapter.addToParentIfResultsReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
itemRow.Retrieve();
|
||||
}
|
||||
|
||||
private SearchQuery getSearchQuery(String[] itemTypes) {
|
||||
SearchQuery query = new SearchQuery();
|
||||
query.setLimit(50);
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.jellyfin.androidtv.search;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.itemhandling.BaseRowItem;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemLauncher;
|
||||
import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.leanback.app.RowsSupportFragment;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
|
||||
public class TextSearchFragment extends Fragment implements TextWatcher, TextView.OnEditorActionListener {
|
||||
private SearchProvider searchProvider;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
searchProvider = new SearchProvider(getContext(), getActivity().getIntent().getBooleanExtra("MusicOnly", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_search_text, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// Add event listeners
|
||||
EditText searchBar = getActivity().findViewById(R.id.search_bar);
|
||||
searchBar.addTextChangedListener(this);
|
||||
searchBar.setOnEditorActionListener(this);
|
||||
|
||||
// Set up result fragment
|
||||
RowsSupportFragment rowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(R.id.results_frame);
|
||||
rowsSupportFragment.setAdapter(searchProvider.getResultsAdapter());
|
||||
|
||||
// Create click listener
|
||||
rowsSupportFragment.setOnItemViewClickedListener((itemViewHolder, item, rowViewHolder, row) -> {
|
||||
if (!(item instanceof BaseRowItem)) return;
|
||||
|
||||
ItemLauncher.launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow) row).getAdapter(), ((BaseRowItem) item).getIndex(), getActivity());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// No implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// No implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
searchProvider.onQueryTextChange(s.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
// Detect keyboard "submit" actions
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH)
|
||||
searchProvider.onQueryTextSubmit(v.getText().toString());
|
||||
|
||||
// Return "false" to automatically close keyboard
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.jellyfin.androidtv.settings;
|
||||
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
@@ -15,17 +13,13 @@ import android.widget.TextView;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.startup.LogonCredentials;
|
||||
import org.jellyfin.androidtv.model.LogonCredentials;
|
||||
import org.jellyfin.androidtv.util.DeviceUtils;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
*/
|
||||
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@Override
|
||||
@@ -34,7 +28,6 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
|
||||
// Load the preferences from an XML resource
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,7 +35,7 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// Set version info
|
||||
TextView ver = (TextView) getActivity().findViewById(R.id.settings_version_info);
|
||||
TextView ver = getActivity().findViewById(R.id.settings_version_info);
|
||||
ver.setText(Utils.getVersionString());
|
||||
|
||||
// conditionally hide options that don't apply
|
||||
@@ -77,8 +70,7 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,8 +92,8 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
|
||||
if (key.equals("pref_send_path_external") && ((CheckBoxPreference)findPreference(key)).isChecked()) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle("W A R N I N G")
|
||||
.setMessage("This feature will only work if you have properly setup your library on the server with network paths or setup Path Substitution AND the external player app you are using can directly access these locations over the network. If playback fails or you didn't understand any of that, disable this option.")
|
||||
.setTitle(getString(R.string.lbl_warning))
|
||||
.setMessage(getString(R.string.msg_external_path))
|
||||
.setPositiveButton(R.string.btn_got_it, null)
|
||||
.show();
|
||||
}
|
||||
@@ -121,7 +113,7 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
if (!TvApp.getApplication().getConfiguredAutoCredentials().getUserDto().getId().equals(TvApp.getApplication().getCurrentUser().getId())) {
|
||||
listPreference.setEnabled(false);
|
||||
pwPrompt.setEnabled(false);
|
||||
extra = getActivity().getString(R.string.lbl_paren_login_as)+TvApp.getApplication().getConfiguredAutoCredentials().getUserDto().getName()+getActivity().getString(R.string.lbl_to_change_paren);
|
||||
extra = getActivity().getString(R.string.lbl_paren_login_as) + TvApp.getApplication().getConfiguredAutoCredentials().getUserDto().getName() + getActivity().getString(R.string.lbl_to_change_paren);
|
||||
}
|
||||
listPreference.setSummary(getActivity().getString(R.string.lbl_login_as) + TvApp.getApplication().getConfiguredAutoCredentials().getUserDto().getName() + extra);
|
||||
} else {
|
||||
@@ -129,6 +121,18 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
pwPrompt.setEnabled(false);
|
||||
}
|
||||
} else {
|
||||
if (listPreference.getKey().equals("pref_video_player")) {
|
||||
boolean isExternal = listPreference.getValue().equals("external");
|
||||
|
||||
// enable/disable other related items
|
||||
Preference direct = findPreference("pref_send_path_external");
|
||||
if (direct != null) direct.setEnabled(isExternal);
|
||||
for (String key : extPlayerVideoDep) {
|
||||
Preference pref = findPreference(key);
|
||||
if (pref != null) pref.setEnabled(!isExternal);
|
||||
}
|
||||
}
|
||||
|
||||
listPreference.setSummary(listPreference.getEntry());
|
||||
}
|
||||
}
|
||||
@@ -139,22 +143,6 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
// enable other live tv direct only options
|
||||
Preference live = findPreference("pref_enable_vlc_livetv");
|
||||
if (live != null) live.setEnabled(cb.isChecked());
|
||||
// Preference shift = findPreference("pref_live_shift");
|
||||
// if (shift != null) shift.setEnabled(!cb.isChecked());
|
||||
} else
|
||||
// if (cb.getKey().equals("pref_live_shift")) {
|
||||
// // enable/disable related options
|
||||
// Preference direct = findPreference("pref_live_direct");
|
||||
// if (direct != null) direct.setEnabled(!cb.isChecked());
|
||||
// } else
|
||||
if (cb.getKey().equals("pref_video_use_external")) {
|
||||
// enable/disable other related items
|
||||
Preference direct = findPreference("pref_send_path_external");
|
||||
if (direct != null) direct.setEnabled(cb.isChecked());
|
||||
for (String key: extPlayerVideoDep) {
|
||||
Preference pref = findPreference(key);
|
||||
if (pref != null) pref.setEnabled(!cb.isChecked());
|
||||
}
|
||||
} else if (cb.getKey().equals("pref_live_tv_use_external")) {
|
||||
// enable/disable other related items
|
||||
for (String key: extPlayerLiveTvDep) {
|
||||
@@ -163,7 +151,5 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
public class DpadPwActivity extends Activity {
|
||||
|
||||
public class DpadPwActivity extends FragmentActivity {
|
||||
|
||||
private long lastKeyDown = Long.MAX_VALUE;
|
||||
private long lastKeyUp = 0;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package org.jellyfin.androidtv.startup;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -28,30 +30,25 @@ import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.GsonJsonSerializer;
|
||||
import org.jellyfin.apiclient.model.apiclient.ServerInfo;
|
||||
import org.jellyfin.apiclient.model.serialization.GsonJsonSerializer;
|
||||
|
||||
/**
|
||||
* Created by Eric on 12/4/2014.
|
||||
*/
|
||||
public class SelectServerFragment extends CustomBrowseFragment {
|
||||
private static final int ENTER_MANUALLY = 0;
|
||||
private List<ServerInfo> mServers = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
|
||||
GsonJsonSerializer serializer = TvApp.getApplication().getSerializer();
|
||||
String[] passedItems = getActivity().getIntent().getStringArrayExtra("Servers");
|
||||
if (passedItems != null) {
|
||||
for (String json : passedItems) {
|
||||
mServers.add((ServerInfo) serializer.DeserializeFromString(json, ServerInfo.class));
|
||||
mServers.add(serializer.DeserializeFromString(json, ServerInfo.class));
|
||||
}
|
||||
}
|
||||
|
||||
mActivity = (BaseActivity) getActivity();
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +56,7 @@ public class SelectServerFragment extends CustomBrowseFragment {
|
||||
super.addAdditionalRows(rowAdapter);
|
||||
|
||||
HeaderItem serverHeader = new HeaderItem(rowAdapter.size(), mApplication.getString(R.string.lbl_select_server));
|
||||
ItemRowAdapter serverAdapter = new ItemRowAdapter(mServers.toArray(new ServerInfo[mServers.size()]), new CardPresenter(), rowAdapter);
|
||||
ItemRowAdapter serverAdapter = new ItemRowAdapter(mServers.toArray(new ServerInfo[0]), new CardPresenter(), rowAdapter);
|
||||
serverAdapter.Retrieve();
|
||||
rowAdapter.add(new ListRow(serverHeader, serverAdapter));
|
||||
|
||||
@@ -67,7 +64,7 @@ public class SelectServerFragment extends CustomBrowseFragment {
|
||||
|
||||
GridButtonPresenter mGridPresenter = new GridButtonPresenter();
|
||||
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
|
||||
gridRowAdapter.add(new GridButton(ENTER_MANUALLY, mApplication.getString(R.string.lbl_enter_manually), R.drawable.edit));
|
||||
gridRowAdapter.add(new GridButton(ENTER_MANUALLY, mApplication.getString(R.string.lbl_enter_manually), R.drawable.tile_edit));
|
||||
rowAdapter.add(new ListRow(gridHeader, gridRowAdapter));
|
||||
}
|
||||
|
||||
@@ -87,9 +84,8 @@ public class SelectServerFragment extends CustomBrowseFragment {
|
||||
@Override
|
||||
public void onMessageReceived(CustomMessage message) {
|
||||
switch (message) {
|
||||
|
||||
case RemoveCurrentItem:
|
||||
((ItemRowAdapter)mCurrentRow.getAdapter()).remove(mCurrentItem);
|
||||
((ItemRowAdapter) mCurrentRow.getAdapter()).remove(mCurrentItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +97,6 @@ public class SelectServerFragment extends CustomBrowseFragment {
|
||||
@Override
|
||||
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
|
||||
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||
|
||||
if (item instanceof GridButton) {
|
||||
switch (((GridButton) item).getId()) {
|
||||
case ENTER_MANUALLY:
|
||||
@@ -115,5 +110,4 @@ public class SelectServerFragment extends CustomBrowseFragment {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.jellyfin.androidtv.startup;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
public class SelectUserActivity extends Activity {
|
||||
|
||||
public class SelectUserActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
@@ -2,13 +2,13 @@ package org.jellyfin.androidtv.startup;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
@@ -18,15 +18,14 @@ import org.jellyfin.androidtv.itemhandling.ItemRowAdapter;
|
||||
import org.jellyfin.androidtv.presentation.CardPresenter;
|
||||
import org.jellyfin.androidtv.presentation.GridButtonPresenter;
|
||||
import org.jellyfin.androidtv.ui.GridButton;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.interaction.GsonJsonSerializer;
|
||||
import org.jellyfin.apiclient.model.apiclient.ServerInfo;
|
||||
import org.jellyfin.apiclient.model.serialization.GsonJsonSerializer;
|
||||
|
||||
public class SelectUserFragment extends CustomBrowseFragment {
|
||||
private static final int ENTER_MANUALLY = 0;
|
||||
@@ -56,8 +55,8 @@ public class SelectUserFragment extends CustomBrowseFragment {
|
||||
|
||||
GridButtonPresenter mGridPresenter = new GridButtonPresenter();
|
||||
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
|
||||
gridRowAdapter.add(new GridButton(ENTER_MANUALLY, mApplication.getString(R.string.lbl_enter_manually), R.drawable.edit));
|
||||
gridRowAdapter.add(new GridButton(SWITCH_SERVER, mApplication.getString(R.string.lbl_switch_server), R.drawable.server));
|
||||
gridRowAdapter.add(new GridButton(ENTER_MANUALLY, mApplication.getString(R.string.lbl_enter_manually), R.drawable.tile_edit));
|
||||
gridRowAdapter.add(new GridButton(SWITCH_SERVER, mApplication.getString(R.string.lbl_switch_server), R.drawable.tile_port_server));
|
||||
rowAdapter.add(new ListRow(gridHeader, gridRowAdapter));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import org.jellyfin.androidtv.BuildConfig;
|
||||
import org.jellyfin.androidtv.R;
|
||||
@@ -24,26 +21,28 @@ import org.jellyfin.androidtv.playback.PlaybackManager;
|
||||
import org.jellyfin.androidtv.util.ProfileHelper;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.AuthenticationHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.AndroidConnectionManager;
|
||||
import org.jellyfin.apiclient.interaction.AndroidDevice;
|
||||
import org.jellyfin.apiclient.interaction.ApiEventListener;
|
||||
import org.jellyfin.apiclient.interaction.ConnectionResult;
|
||||
import org.jellyfin.apiclient.interaction.IConnectionManager;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.interaction.AndroidConnectionManager;
|
||||
import org.jellyfin.apiclient.interaction.AndroidDevice;
|
||||
import org.jellyfin.apiclient.interaction.GsonJsonSerializer;
|
||||
import org.jellyfin.apiclient.interaction.VolleyHttpClient;
|
||||
import org.jellyfin.apiclient.model.apiclient.ConnectionState;
|
||||
import org.jellyfin.apiclient.model.dto.UserDto;
|
||||
import org.jellyfin.apiclient.model.logging.ILogger;
|
||||
import org.jellyfin.apiclient.model.serialization.IJsonSerializer;
|
||||
import org.jellyfin.apiclient.model.serialization.GsonJsonSerializer;
|
||||
import org.jellyfin.apiclient.model.session.ClientCapabilities;
|
||||
import org.jellyfin.apiclient.model.session.GeneralCommandType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class StartupActivity extends Activity {
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
public class StartupActivity extends FragmentActivity {
|
||||
|
||||
private static final int NETWORK_PERMISSION = 1;
|
||||
private TvApp application;
|
||||
@@ -60,7 +59,7 @@ public class StartupActivity extends Activity {
|
||||
|
||||
//Migrate prefs
|
||||
if (Integer.parseInt(application.getConfigVersion()) < 2) {
|
||||
application.getSystemPrefs().edit().putString("sys_pref_config_version", "2").commit();
|
||||
application.getSystemPrefs().edit().putString("sys_pref_config_version", "2").apply();
|
||||
}
|
||||
if (Integer.parseInt(application.getConfigVersion()) < 3) {
|
||||
application.getPrefs().edit().putString("pref_max_bitrate", "0").apply();
|
||||
@@ -71,12 +70,12 @@ public class StartupActivity extends Activity {
|
||||
application.getPrefs().edit().putBoolean("pref_enable_info_panel", false).apply();
|
||||
application.getSystemPrefs().edit().putString("sys_pref_config_version", "4").apply();
|
||||
}
|
||||
// if (Integer.parseInt(application.getConfigVersion()) < 5) {
|
||||
// application.getPrefs().edit().putBoolean("pref_live_shift", true).apply();
|
||||
// application.getPrefs().edit().putBoolean("pref_live_direct", false).apply();
|
||||
// application.getSystemPrefs().edit().putString("sys_pref_config_version", "5").apply();
|
||||
// }
|
||||
if (Integer.parseInt(application.getConfigVersion()) < 5) {
|
||||
boolean useExternal = application.getPrefs().getBoolean("pref_video_use_external", false);
|
||||
|
||||
application.getPrefs().edit().putString("pref_video_player", useExternal ? "external" : "auto").apply();
|
||||
application.getSystemPrefs().edit().putString("sys_pref_config_version", "5").apply();
|
||||
}
|
||||
|
||||
//Ensure we have prefs
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||
@@ -97,14 +96,12 @@ public class StartupActivity extends Activity {
|
||||
// go straight into last connection
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
|
||||
} else {
|
||||
//clear audio queue in case left over from last run
|
||||
MediaManager.clearAudioQueue();
|
||||
MediaManager.clearVideoQueue();
|
||||
establishConnection(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,12 +150,8 @@ public class StartupActivity extends Activity {
|
||||
capabilities.setDeviceProfile(new AndroidProfile(ProfileHelper.getProfileOptions()));
|
||||
capabilities.setSupportsMediaControl(true);
|
||||
capabilities.setSupportedCommands(supportedCommands);
|
||||
// TODO: Add new icon url
|
||||
// capabilities.setIconUrl("");
|
||||
|
||||
IJsonSerializer jsonSerializer = new GsonJsonSerializer();
|
||||
|
||||
|
||||
GsonJsonSerializer jsonSerializer = new GsonJsonSerializer();
|
||||
ApiEventListener apiEventListener = new TvApiEventListener();
|
||||
|
||||
final IConnectionManager connectionManager = new AndroidConnectionManager(application,
|
||||
@@ -172,8 +165,7 @@ public class StartupActivity extends Activity {
|
||||
apiEventListener);
|
||||
|
||||
application.setConnectionManager(connectionManager);
|
||||
application.setSerializer((GsonJsonSerializer) jsonSerializer);
|
||||
|
||||
application.setSerializer(jsonSerializer);
|
||||
application.setPlaybackManager(new PlaybackManager(new AndroidDevice(application), logger));
|
||||
|
||||
//See if we are coming in via direct entry
|
||||
@@ -188,11 +180,21 @@ public class StartupActivity extends Activity {
|
||||
connectionManager.Connect(application.getConfiguredAutoCredentials().getServerInfo(), new Response<ConnectionResult>() {
|
||||
@Override
|
||||
public void onResponse(ConnectionResult response) {
|
||||
// Saved server login is unavailable
|
||||
if (response.getState() == ConnectionState.Unavailable) {
|
||||
Utils.showToast( activity, "Unable to connect to configured server "+application.getConfiguredAutoCredentials().getServerInfo().getName());
|
||||
connectAutomatically(connectionManager, activity);
|
||||
Utils.showToast(activity, R.string.msg_error_server_unavailable + ": " + application.getConfiguredAutoCredentials().getServerInfo().getName());
|
||||
AuthenticationHelper.automaticSignIn(connectionManager, activity);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the server version
|
||||
if (!response.getServers().isEmpty() &&
|
||||
!AuthenticationHelper.isSupportedServerVersion(response.getServers().get(0))) {
|
||||
Utils.showToast(activity, activity.getString(R.string.msg_error_server_version, TvApp.MINIMUM_SERVER_VERSION));
|
||||
AuthenticationHelper.automaticSignIn(connectionManager, activity);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connected to server - load user and prompt for pw if necessary
|
||||
application.setLoginApiClient(response.getApiClient());
|
||||
response.getApiClient().GetUserAsync(application.getConfiguredAutoCredentials().getUserDto().getId(), new Response<UserDto>() {
|
||||
@@ -208,16 +210,13 @@ public class StartupActivity extends Activity {
|
||||
Utils.processPasswordEntry(activity, response, application.getDirectItemId());
|
||||
} else {
|
||||
//Can just go right into details
|
||||
|
||||
Intent detailsIntent = new Intent(activity, FullDetailsActivity.class);
|
||||
detailsIntent.putExtra("ItemId", application.getDirectItemId());
|
||||
startActivity(detailsIntent);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (response.getHasPassword() && application.getPrefs().getBoolean("pref_auto_pw_prompt", false)) {
|
||||
Utils.processPasswordEntry(activity, response);
|
||||
|
||||
} else {
|
||||
Intent intent = new Intent(activity, MainActivity.class);
|
||||
activity.startActivity(intent);
|
||||
@@ -228,11 +227,11 @@ public class StartupActivity extends Activity {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
application.getLogger().ErrorException("Error Signing in", exception);
|
||||
Utils.reportError(activity, "Error Signing In");
|
||||
Utils.showToast(activity, R.string.msg_error_signin);
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
connectAutomatically(connectionManager, activity);
|
||||
AuthenticationHelper.automaticSignIn(connectionManager, activity);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
@@ -241,29 +240,12 @@ public class StartupActivity extends Activity {
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
Utils.showToast( activity, "Unable to connect to configured server "+application.getConfiguredAutoCredentials().getServerInfo().getName());
|
||||
connectAutomatically(connectionManager, activity);
|
||||
Utils.showToast( activity, R.string.msg_error_connecting_server + ": " + application.getConfiguredAutoCredentials().getServerInfo().getName());
|
||||
AuthenticationHelper.automaticSignIn(connectionManager, activity);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
connectAutomatically(connectionManager, activity);
|
||||
AuthenticationHelper.automaticSignIn(connectionManager, activity);
|
||||
}
|
||||
}
|
||||
|
||||
private void connectAutomatically(final IConnectionManager connectionManager, final Activity activity){
|
||||
connectionManager.Connect(new Response<ConnectionResult>() {
|
||||
@Override
|
||||
public void onResponse(final ConnectionResult response) {
|
||||
AuthenticationHelper.handleConnectionResponse(connectionManager, activity, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
Utils.reportError(activity, "Error connecting");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package org.jellyfin.androidtv.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -32,7 +33,7 @@ public class AudioDelayPopup {
|
||||
mPopup = new PopupWindow(layout, WIDTH, HEIGHT);
|
||||
mPopup.setFocusable(true);
|
||||
mPopup.setOutsideTouchable(true);
|
||||
mPopup.setBackgroundDrawable(new BitmapDrawable()); // necessary for popup to dismiss
|
||||
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // necessary for popup to dismiss
|
||||
|
||||
mAnchor = anchor;
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ public class ClockUserView extends RelativeLayout {
|
||||
if (TvApp.getApplication().getCurrentUser().getPrimaryImageTag() != null) {
|
||||
Picasso.with(context)
|
||||
.load(ImageUtils.getPrimaryImageUrl(TvApp.getApplication().getCurrentUser(), TvApp.getApplication().getApiClient()))
|
||||
.error(R.drawable.user)
|
||||
.error(R.drawable.ic_user)
|
||||
.resize(30,30)
|
||||
.centerInside()
|
||||
.into(userImage);
|
||||
} else {
|
||||
userImage.setImageResource(R.drawable.user);
|
||||
userImage.setImageResource(R.drawable.ic_user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package org.jellyfin.androidtv.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -45,7 +46,7 @@ public class DisplayPrefsPopup {
|
||||
mPopup = new PopupWindow(layout, WIDTH, HEIGHT);
|
||||
mPopup.setFocusable(true);
|
||||
mPopup.setOutsideTouchable(true);
|
||||
mPopup.setBackgroundDrawable(new BitmapDrawable()); // necessary for popup to dismiss
|
||||
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // necessary for popup to dismiss
|
||||
mPopup.setAnimationStyle(R.style.PopupSlideInRight);
|
||||
|
||||
mAnchor = anchor;
|
||||
|
||||
@@ -4,8 +4,8 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import androidx.core.app.ActivityOptionsCompat;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import android.text.Layout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.SoundEffectConstants;
|
||||
|
||||
@@ -2,13 +2,15 @@ package org.jellyfin.androidtv.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
|
||||
/**
|
||||
* Created by Eric on 2/21/2015.
|
||||
*/
|
||||
public class GenreButton extends AppCompatTextView {
|
||||
public GenreButton(Context context, Typeface font, int size, String text, String itemType) {
|
||||
public GenreButton(Context context, Typeface font, int size, String text, BaseItemType itemType) {
|
||||
super(context);
|
||||
setTypeface(font);
|
||||
setTextSize(size);
|
||||
|
||||
@@ -22,7 +22,9 @@ public class GridButton {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getImageIndex() {return imageIndex;}
|
||||
public int getImageIndex() {
|
||||
return imageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.jellyfin.androidtv.ui;
|
||||
|
||||
import android.app.Fragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.support.v17.leanback.widget.ObjectAdapter;
|
||||
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||
import android.support.v17.leanback.widget.Presenter;
|
||||
import android.support.v17.leanback.widget.Row;
|
||||
import android.support.v17.leanback.widget.RowPresenter;
|
||||
import androidx.leanback.widget.ObjectAdapter;
|
||||
import androidx.leanback.widget.OnItemViewClickedListener;
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener;
|
||||
import androidx.leanback.widget.Presenter;
|
||||
import androidx.leanback.widget.Row;
|
||||
import androidx.leanback.widget.RowPresenter;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.jellyfin.androidtv.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
@@ -47,23 +47,21 @@ public class ImageButton extends AppCompatImageView {
|
||||
mHelpView = helpView;
|
||||
mHelpText = helpText;
|
||||
setOnFocusChangeListener(focusChangeListener);
|
||||
|
||||
}
|
||||
|
||||
public void setHelpView(TextView view) {
|
||||
mHelpView = view;
|
||||
}
|
||||
public void setHelpText(String text) { mHelpText = text; }
|
||||
|
||||
public void setHelpText(String text) {
|
||||
mHelpText = text;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
mState = state;
|
||||
if (mSecondaryImage > 0) setImageResource(mState == STATE_SECONDARY ? mSecondaryImage : mPrimaryImage);
|
||||
}
|
||||
|
||||
public void toggleState() {
|
||||
setState(mState == STATE_PRIMARY ? STATE_SECONDARY : STATE_PRIMARY);
|
||||
}
|
||||
|
||||
private OnFocusChangeListener focusChangeListener = new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
@@ -78,7 +76,9 @@ public class ImageButton extends AppCompatImageView {
|
||||
}
|
||||
};
|
||||
|
||||
public void setGotFocusListener(GotFocusEvent event) { mGotFocusListener = event; }
|
||||
public void setGotFocusListener(GotFocusEvent event) {
|
||||
mGotFocusListener = event;
|
||||
}
|
||||
|
||||
public void setPrimaryImage(int mPrimaryImage) {
|
||||
this.mPrimaryImage = mPrimaryImage;
|
||||
|
||||
@@ -44,8 +44,7 @@ public class ItemListView extends FrameLayout {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
inflater.inflate(R.layout.item_list, this);
|
||||
mContext = context;
|
||||
mList = (LinearLayout) findViewById(R.id.songList);
|
||||
|
||||
mList = findViewById(R.id.songList);
|
||||
}
|
||||
|
||||
public void setRowSelectedListener(ItemRowView.RowSelectedListener listener) { mRowSelectedListener = listener; }
|
||||
@@ -61,9 +60,6 @@ public class ItemListView extends FrameLayout {
|
||||
for (BaseItemDto item : items) {
|
||||
addItem(item, i++);
|
||||
}
|
||||
//Throw in another item just to provide some padding at the end of the scroll
|
||||
mList.addView(new TextView(mContext));
|
||||
|
||||
}
|
||||
|
||||
public void addItem(BaseItemDto item, int ndx) {
|
||||
@@ -97,7 +93,8 @@ public class ItemListView extends FrameLayout {
|
||||
if (response.getItems() != null) {
|
||||
int i = 0;
|
||||
for (BaseItemDto item : response.getItems()) {
|
||||
View view = mList.getChildAt(i+1); // we have title view as first one
|
||||
// we have title view as first one
|
||||
View view = mList.getChildAt(i + 1);
|
||||
if (view instanceof ItemRowView) {
|
||||
ItemRowView row = (ItemRowView) view;
|
||||
row.setItem(item, i++);
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.TvApp;
|
||||
import org.jellyfin.androidtv.itemhandling.BaseRowItem;
|
||||
import org.jellyfin.androidtv.util.InfoLayoutHelper;
|
||||
import org.jellyfin.apiclient.model.dto.BaseItemType;
|
||||
|
||||
/**
|
||||
* Created by Eric on 7/27/2015.
|
||||
@@ -49,7 +50,7 @@ public class ItemPanel extends RelativeLayout {
|
||||
|
||||
public void setItem(BaseRowItem item) {
|
||||
if (item != null) {
|
||||
title.setText(item.getFullName() + (item.getItemType() == BaseRowItem.ItemType.BaseItem && "Episode".equals(item.getBaseItem().getType()) ? " - " + item.getName() : ""));
|
||||
title.setText(item.getFullName() + (item.getItemType() == BaseRowItem.ItemType.BaseItem && item.getBaseItemType() == BaseItemType.Episode ? " - " + item.getName() : ""));
|
||||
if (TvApp.getApplication().getCurrentActivity() != null) InfoLayoutHelper.addInfoRow(TvApp.getApplication().getCurrentActivity(), item, infoRow, true, true);
|
||||
summary.setText(item.getSummary());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.jellyfin.androidtv.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
@@ -29,7 +28,6 @@ public class ItemRowView extends FrameLayout {
|
||||
TextView mItemName;
|
||||
TextView mExtraName;
|
||||
TextView mRunTime;
|
||||
TextView mDivider;
|
||||
TextView mWatchedMark;
|
||||
Drawable normalBackground;
|
||||
|
||||
@@ -40,7 +38,6 @@ public class ItemRowView extends FrameLayout {
|
||||
|
||||
RowSelectedListener rowSelectedListener;
|
||||
RowClickedListener rowClickedListener;
|
||||
ItemRowView us;
|
||||
|
||||
public ItemRowView(Context context) {
|
||||
super(context);
|
||||
@@ -58,12 +55,12 @@ public class ItemRowView extends FrameLayout {
|
||||
this.rowSelectedListener = rowSelectedListener;
|
||||
this.rowClickedListener = rowClickedListener;
|
||||
setItem(song, ndx);
|
||||
us = this;
|
||||
final ItemRowView itemRowView = this;
|
||||
setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
playSoundEffect(SoundEffectConstants.CLICK);
|
||||
if (rowClickedListener != null) rowClickedListener.onRowClicked(us);
|
||||
if (rowClickedListener != null) rowClickedListener.onRowClicked(itemRowView);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -72,16 +69,14 @@ public class ItemRowView extends FrameLayout {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
inflater.inflate(R.layout.item_row, this);
|
||||
mContext = context;
|
||||
mWholeRow = (RelativeLayout) findViewById(R.id.wholeRow);
|
||||
mIndexNo = (TextView) findViewById(R.id.indexNo);
|
||||
mItemName = (TextView) findViewById(R.id.songName);
|
||||
mExtraName = (TextView) findViewById(R.id.artistName);
|
||||
mRunTime = (TextView) findViewById(R.id.runTime);
|
||||
mDivider = (TextView) findViewById(R.id.divider);
|
||||
mWatchedMark = (TextView) findViewById(R.id.watchedMark);
|
||||
mWholeRow = findViewById(R.id.wholeRow);
|
||||
mIndexNo = findViewById(R.id.indexNo);
|
||||
mItemName = findViewById(R.id.songName);
|
||||
mExtraName = findViewById(R.id.artistName);
|
||||
mRunTime = findViewById(R.id.runTime);
|
||||
mWatchedMark = findViewById(R.id.watchedMark);
|
||||
normalBackground = mWholeRow.getBackground();
|
||||
setFocusable(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,15 +95,14 @@ public class ItemRowView extends FrameLayout {
|
||||
mBaseItem = item;
|
||||
ourIndex = ndx + 1;
|
||||
mIndexNo.setText(Integer.toString(ourIndex));
|
||||
switch (item.getType()) {
|
||||
case "Audio":
|
||||
switch (item.getBaseItemType()) {
|
||||
case Audio:
|
||||
mItemName.setText(item.getName());
|
||||
String artist = item.getArtists() != null && item.getArtists().size() > 0 ? item.getArtists().get(0) : !TextUtils.isEmpty(item.getAlbumArtist()) ? item.getAlbumArtist() : null;
|
||||
if (!TextUtils.isEmpty(artist)) {
|
||||
mExtraName.setText(artist);
|
||||
} else {
|
||||
mExtraName.setVisibility(GONE);
|
||||
mDivider.setVisibility(GONE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -119,7 +113,6 @@ public class ItemRowView extends FrameLayout {
|
||||
} else {
|
||||
mItemName.setText(item.getName());
|
||||
mExtraName.setVisibility(GONE);
|
||||
mDivider.setVisibility(GONE);
|
||||
}
|
||||
updateWatched();
|
||||
break;
|
||||
@@ -151,9 +144,9 @@ public class ItemRowView extends FrameLayout {
|
||||
|
||||
public boolean setPlaying(boolean playing) {
|
||||
if (playing) {
|
||||
mIndexNo.setBackgroundResource(R.drawable.eq_animation);
|
||||
// TODO use decent animation for equalizer icon
|
||||
mIndexNo.setBackgroundResource(R.drawable.ic_play);
|
||||
mIndexNo.setText("");
|
||||
((AnimationDrawable)mIndexNo.getBackground()).start();
|
||||
} else {
|
||||
mIndexNo.setBackgroundResource(R.drawable.blank10x10);
|
||||
mIndexNo.setText(Integer.toString(ourIndex));
|
||||
@@ -176,5 +169,4 @@ public class ItemRowView extends FrameLayout {
|
||||
public static class RowClickedListener {
|
||||
public void onRowClicked(ItemRowView row) {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -62,7 +62,7 @@ public class LiveProgramDetailPopup {
|
||||
mPopup = new PopupWindow(layout, width, NORMAL_HEIGHT);
|
||||
mPopup.setFocusable(true);
|
||||
mPopup.setOutsideTouchable(true);
|
||||
mPopup.setBackgroundDrawable(new BitmapDrawable()); // necessary for popup to dismiss
|
||||
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // necessary for popup to dismiss
|
||||
mPopup.setAnimationStyle(R.style.PopupSlideInTop);
|
||||
mDTitle = layout.findViewById(R.id.title);
|
||||
mDTitle.setTypeface(roboto);
|
||||
|
||||
@@ -64,7 +64,6 @@ public class NowPlayingBug extends FrameLayout {
|
||||
Intent np = new Intent(TvApp.getApplication(), AudioNowPlayingActivity.class);
|
||||
TvApp.getApplication().getCurrentActivity().startActivity(np);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -89,7 +88,7 @@ public class NowPlayingBug extends FrameLayout {
|
||||
|
||||
@Override
|
||||
public void onProgress(long pos) {
|
||||
if (isShown()) npStatus.setText(TimeUtils.formatMillis(pos) + "/" + currentDuration);
|
||||
if (isShown()) setStatus(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,7 +96,7 @@ public class NowPlayingBug extends FrameLayout {
|
||||
if (hasQueue) {
|
||||
// may have just added one so update display
|
||||
setInfo(MediaManager.getCurrentAudioItem());
|
||||
npStatus.setText(TimeUtils.formatMillis(MediaManager.getCurrentAudioPosition()) + "/" + currentDuration);
|
||||
setStatus(MediaManager.getCurrentAudioPosition());
|
||||
setVisibility(VISIBLE);
|
||||
} else {
|
||||
setVisibility(GONE);
|
||||
@@ -113,7 +112,7 @@ public class NowPlayingBug extends FrameLayout {
|
||||
MediaManager.addAudioEventListener(listener);
|
||||
if (manageVisibility()) {
|
||||
setInfo(MediaManager.getCurrentAudioItem());
|
||||
npStatus.setText(TimeUtils.formatMillis(MediaManager.getCurrentAudioPosition()) + "/" + currentDuration);
|
||||
setStatus(MediaManager.getCurrentAudioPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,17 +123,19 @@ public class NowPlayingBug extends FrameLayout {
|
||||
if (!isInEditMode()) {
|
||||
// unhook our events
|
||||
MediaManager.removeAudioEventListener(listener);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void setInfo(BaseItemDto item) {
|
||||
if (item == null) return;
|
||||
|
||||
Picasso.with(context).load(ImageUtils.getPrimaryImageUrl(item, TvApp.getApplication().getApiClient())).error(R.drawable.audioicon).resize(35,35).centerInside().into(npIcon);
|
||||
Picasso.with(context).load(ImageUtils.getPrimaryImageUrl(item, TvApp.getApplication().getApiClient())).error(R.drawable.ic_album).resize(35,35).centerInside().into(npIcon);
|
||||
currentDuration = TimeUtils.formatMillis(item.getRunTimeTicks() != null ? item.getRunTimeTicks() / 10000 : 0);
|
||||
npDesc.setText(item.getAlbumArtist() != null ? item.getAlbumArtist() : item.getName());
|
||||
}
|
||||
|
||||
private void setStatus(long pos) {
|
||||
npStatus.setText(String.format(getResources().getString(R.string.lbl_status), TimeUtils.formatMillis(pos), currentDuration));
|
||||
}
|
||||
|
||||
public boolean manageVisibility() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user