Compare commits

...

40 Commits

Author SHA1 Message Date
Niels van Velzen
1873497fec Fix RewriteMediaManager never used in production builds
(cherry picked from commit 75b6b12147)
2023-12-18 22:21:02 +01:00
Niels van Velzen
32849f5279 Fix crash when starting player where playback controller may be null 2023-12-17 15:56:02 +01:00
queeup
0c1aac96d7 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-17 08:30:20 -05:00
queeup
7fefb735d9 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-16 19:30:20 -05:00
queeup
00c59f8e58 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-16 15:30:20 -05:00
renovate[bot]
c87121502c chore(deps): update gradle/gradle-build-action action to v2.11.0 2023-12-15 19:04:04 +01:00
renovate[bot]
636facc8b2 fix(deps): update dependency androidx.activity:activity-ktx to v1.8.2 2023-12-15 19:03:20 +01:00
renovate[bot]
98ac4d3eda chore(deps): update github/codeql-action action to v3 2023-12-15 19:01:31 +01:00
renovate[bot]
d3cd69530a chore(deps): update actions/upload-artifact action to v4 2023-12-15 19:01:06 +01:00
queeup
9937ad3ddd Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-14 19:30:20 -05:00
queeup
9a6fc333a1 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-13 21:30:19 -05:00
xdlivecat
5811b99b35 Translated using Weblate (Croatian)
Currently translated at 77.1% (385 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/hr/
2023-12-13 21:30:19 -05:00
queeup
72c97635e3 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-12 18:30:19 -05:00
Alexander Gramiak
afddc96789 util: Don't use TZ offset for media Premiere Date
TimeUtils.getDate internally calls Date.format, which translates the UTC
timestamp into local TZ timestamp. If a user is in a TZ with negative offset,
then the Premiere Date will be presented as a day earlier.

Example with TZ of UTC-6:

item.getPremiereDate()                    => 2023-12-09T00:00
TimeUtils.getDate(item.getPremiereDate()) => Sat Dec 08 18:00:00 CST 2023

Fixes #3190.
2023-12-12 15:27:24 +01:00
guiweber
a8ef52bca9 Update InfoLayoutHelper.java
Keep displaying codec for HDR content
2023-12-12 15:24:59 +01:00
Lukáš Kucharczyk
a0c81df329 Translated using Weblate (Czech)
Currently translated at 100.0% (499 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/cs/
2023-12-12 08:30:19 -05:00
renovate[bot]
c4db326da6 Update dependency com.vanniktech:blurhash to v0.2.0 2023-12-12 11:53:25 +01:00
queeup
7d53fee677 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-11 17:30:18 -05:00
Niels van Velzen
fa10019f8b Fix rare case where splash screen could get stuck 2023-12-11 14:35:29 +01:00
queeup
b4fbd82c21 Translated using Weblate (Turkish)
Currently translated at 99.5% (497 of 499 strings)

Translation: Jellyfin Android/Jellyfin Android TV
Translate-URL: https://translate.jellyfin.org/projects/jellyfin-android/jellyfin-androidtv/tr/
2023-12-10 20:30:18 -05:00
renovate[bot]
db6c23877f Update dependency io.gitlab.arturbosch.detekt to v1.23.4 2023-12-10 15:10:20 +01:00
Niels van Velzen
f8fd9765ac Use androidx.work:work-runtime (non ktx version) 2023-12-10 15:10:10 +01:00
renovate[bot]
8b5ad52fb9 Update dependency androidx.work:work-runtime-ktx to v2.9.0 2023-12-10 15:10:10 +01:00
renovate[bot]
90f2623c9a Update dependency androidx.compose.compiler:compiler to v1.5.6 2023-12-10 15:03:37 +01:00
renovate[bot]
9d2dacdac3 Update Kotlin 2023-12-10 15:03:29 +01:00
renovate[bot]
174ed2e281 Update dependency androidx.media3:media3-session to v1.2.0 2023-12-10 15:00:36 +01:00
renovate[bot]
8a0b97619b Update coil to v2.5.0 2023-12-10 14:59:23 +01:00
renovate[bot]
3d2c2c7363 Update kotest to v5.8.0 2023-12-10 14:59:13 +01:00
renovate[bot]
b2fb409605 Update dependency com.android.tools.build:gradle to v8.2.0 2023-12-10 14:58:44 +01:00
renovate[bot]
f14b8ed97b Update actions/stale action to v9 2023-12-10 14:57:12 +01:00
renovate[bot]
92e91ab217 Update actions/setup-java action to v4 2023-12-10 14:57:00 +01:00
renovate[bot]
4dd363b0cf Update CI dependencies 2023-12-10 14:56:15 +01:00
renovate[bot]
a083dfcc04 Update dependency androidx.window:window to v1.2.0 2023-12-10 14:48:39 +01:00
renovate[bot]
d3dba03ee0 Update dependency com.android.tools:desugar_jdk_libs to v2.0.4 2023-12-10 14:45:11 +01:00
renovate[bot]
56d6661906 Update dependency androidx.fragment:fragment-ktx to v1.6.2 2023-12-10 14:44:44 +01:00
renovate[bot]
2fd2efa581 Update dependency androidx.activity:activity-ktx to v1.8.1 2023-12-10 14:43:51 +01:00
renovate[bot]
d866b1bce1 Update aboutlibraries to v10.9.2 2023-12-10 14:42:05 +01:00
renovate[bot]
238a9d27fd Update dependency gradle to v8.5 2023-12-10 14:36:40 +01:00
Niels van Velzen
004b39e89b Close video player on resume with invalid state
In some cases the video player may be stopped when the activity is paused. This ends up crashing the app on resume. Check for this kind of invalid state and close the video player to prevent crashing.
2023-12-10 14:35:52 +01:00
Niels van Velzen
d9eb849038 Fix system screensaver starting during video playback 2023-12-10 14:35:43 +01:00
19 changed files with 172 additions and 122 deletions

View File

@@ -17,18 +17,18 @@ jobs:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Java
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a # v2.9.0
uses: gradle/gradle-build-action@8cbcb9948b5892387aed077daf6f90e1f0ba5b27 # v2.11.0
- name: Assemble debug APKs
run: ./gradlew assembleDebug
- name: Create publish bundle
run: mkdir -p build/gh-app-publish/; find app/build/ -iname "*.apk" -exec mv "{}" build/gh-app-publish/ \;
- name: Upload artifacts
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
with:
name: build-artifacts
retention-days: 14

View File

@@ -19,16 +19,16 @@ jobs:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Java
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a # v2.9.0
uses: gradle/gradle-build-action@8cbcb9948b5892387aed077daf6f90e1f0ba5b27 # v2.11.0
- name: Run detekt and lint tasks
run: ./gradlew detekt lint
- name: Upload SARIF files
uses: github/codeql-action/upload-sarif@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2.22.4
uses: github/codeql-action/upload-sarif@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11
if: ${{ always() }}
with:
sarif_file: .

View File

@@ -14,12 +14,12 @@ jobs:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Java
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a # v2.9.0
uses: gradle/gradle-build-action@8cbcb9948b5892387aed077daf6f90e1f0ba5b27 # v2.11.0
- name: Set JELLYFIN_VERSION
run: echo "JELLYFIN_VERSION=$(echo ${GITHUB_REF#refs/tags/v} | tr / -)" >> $GITHUB_ENV
- name: Assemble release files

View File

@@ -18,11 +18,11 @@ jobs:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Java
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a # v2.9.0
uses: gradle/gradle-build-action@8cbcb9948b5892387aed077daf6f90e1f0ba5b27 # v2.11.0
- name: Run test task
run: ./gradlew test

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-22.04
if: ${{ contains(github.repository_owner, 'jellyfin') }}
steps:
- uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
days-before-stale: 120

View File

@@ -18,7 +18,7 @@ class SessionInitializer : Initializer<Unit> {
// Restore system session
ProcessLifecycleOwner.get().lifecycleScope.launch {
koin.get<SessionRepository>().restoreSession()
koin.get<SessionRepository>().restoreSession(destroyOnly = false)
}
}

View File

@@ -1,10 +1,12 @@
package org.jellyfin.androidtv.auth.repository
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.jellyfin.androidtv.auth.apiclient.ApiBinder
import org.jellyfin.androidtv.auth.store.AuthenticationPreferences
import org.jellyfin.androidtv.auth.store.AuthenticationStore
@@ -39,7 +41,7 @@ interface SessionRepository {
val currentSession: StateFlow<Session?>
val state: StateFlow<SessionRepositoryState>
suspend fun restoreSession()
suspend fun restoreSession(destroyOnly: Boolean)
suspend fun switchCurrentSession(serverId: UUID, userId: UUID): Boolean
fun destroyCurrentSession()
}
@@ -61,23 +63,28 @@ class SessionRepositoryImpl(
private val _state = MutableStateFlow(SessionRepositoryState.READY)
override val state = _state.asStateFlow()
override suspend fun restoreSession(): Unit = currentSessionMutex.withLock {
Timber.i("Restoring session")
_state.value = SessionRepositoryState.RESTORING_SESSION
override suspend fun restoreSession(destroyOnly: Boolean): Unit = withContext(NonCancellable) {
currentSessionMutex.withLock {
Timber.i("Restoring session")
if (authenticationPreferences[AuthenticationPreferences.alwaysAuthenticate]) return destroyCurrentSession()
_state.value = SessionRepositoryState.RESTORING_SESSION
when (authenticationPreferences[AuthenticationPreferences.autoLoginUserBehavior]) {
DISABLED -> destroyCurrentSession()
LAST_USER -> setCurrentSession(createLastUserSession())
SPECIFIC_USER -> {
val serverId = authenticationPreferences[AuthenticationPreferences.autoLoginServerId].toUUIDOrNull()
val userId = authenticationPreferences[AuthenticationPreferences.autoLoginUserId].toUUIDOrNull()
if (serverId != null && userId != null) setCurrentSession(createUserSession(serverId, userId))
val alwaysAuthenticate = authenticationPreferences[AuthenticationPreferences.alwaysAuthenticate]
val autoLoginBehavior = authenticationPreferences[AuthenticationPreferences.autoLoginUserBehavior]
when {
alwaysAuthenticate -> destroyCurrentSession()
autoLoginBehavior == DISABLED -> destroyCurrentSession()
autoLoginBehavior == LAST_USER && !destroyOnly -> setCurrentSession(createLastUserSession())
autoLoginBehavior == SPECIFIC_USER && !destroyOnly -> {
val serverId = authenticationPreferences[AuthenticationPreferences.autoLoginServerId].toUUIDOrNull()
val userId = authenticationPreferences[AuthenticationPreferences.autoLoginUserId].toUUIDOrNull()
if (serverId != null && userId != null) setCurrentSession(createUserSession(serverId, userId))
}
}
}
_state.value = SessionRepositoryState.READY
_state.value = SessionRepositoryState.READY
}
}
override suspend fun switchCurrentSession(serverId: UUID, userId: UUID): Boolean {
@@ -93,6 +100,7 @@ class SessionRepositoryImpl(
val session = createUserSession(serverId, userId)
if (session == null) {
Timber.w("Could not switch to non-existing session for user $userId")
_state.value = SessionRepositoryState.READY
return false
}

View File

@@ -35,7 +35,7 @@ val playbackModule = module {
factory {
val preferences = get<UserPreferences>()
val useRewrite = preferences[UserPreferences.playbackRewriteAudioEnabled] && BuildConfig.DEVELOPMENT
val useRewrite = preferences[UserPreferences.playbackRewriteAudioEnabled]
if (useRewrite) get<RewriteMediaManager>()
else get<LegacyMediaManager>()

View File

@@ -18,11 +18,20 @@ class ScreensaverViewModel(
) : ViewModel() {
private var timer: Job? = null
private var locks = 0
val enabled get() = userPreferences[UserPreferences.screensaverInAppEnabled]
// Preferences
val inAppEnabled get() = userPreferences[UserPreferences.screensaverInAppEnabled]
private val timeout get() = userPreferences[UserPreferences.screensaverInAppTimeout].milliseconds
// State
private val _visible = MutableStateFlow(false)
val visible get() = _visible.asStateFlow()
private val _keepScreenOn = MutableStateFlow(inAppEnabled)
val keepScreenOn get() = _keepScreenOn.asStateFlow()
var activityPaused: Boolean = false
set(value) {
field = value
@@ -38,17 +47,20 @@ class ScreensaverViewModel(
timer?.cancel()
// Hide when interacted with allowed cancelation or when disabled
if (_visible.value && (canCancel || !enabled || activityPaused)) {
if (_visible.value && (canCancel || !inAppEnabled || activityPaused)) {
_visible.value = false
}
// Create new timer to show screensaver when enabled
if (enabled && !activityPaused && locks == 0) {
if (inAppEnabled && !activityPaused && locks == 0) {
timer = viewModelScope.launch {
delay(timeout)
_visible.value = true
}
}
// Update KEEP_SCREEN_ON flag value
_keepScreenOn.value = inAppEnabled || locks > 0
}
/**

View File

@@ -56,6 +56,12 @@ class MainActivity : FragmentActivity() {
if (!validateAuthentication()) return
screensaverViewModel.keepScreenOn.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.onEach { keepScreenOn ->
if (keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}.launchIn(lifecycleScope)
onBackPressedDispatcher.addCallback(this, backPressedCallback)
supportFragmentManager.addOnBackStackChangedListener {
@@ -87,9 +93,6 @@ class MainActivity : FragmentActivity() {
applyTheme()
screensaverViewModel.activityPaused = false
if (screensaverViewModel.enabled) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun validateAuthentication(): Boolean {
@@ -114,7 +117,7 @@ class MainActivity : FragmentActivity() {
lifecycleScope.launch {
Timber.d("MainActivity stopped")
sessionRepository.restoreSession()
sessionRepository.restoreSession(destroyOnly = true)
}
}

View File

@@ -127,7 +127,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
private List<ChannelInfoDto> mAllChannels;
private String mFirstFocusChannelId;
private PlaybackController mPlaybackController;
private List<org.jellyfin.sdk.model.api.BaseItemDto> mItemsToPlay;
private Animation fadeOut;
@@ -195,7 +194,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
int mediaPosition = videoQueueManager.getValue().getCurrentMediaPosition();
playbackControllerContainer.getValue().setPlaybackController(new PlaybackController(mItemsToPlay, this, mediaPosition));
mPlaybackController = playbackControllerContainer.getValue().getPlaybackController();
// setup fade task
mHideTask = () -> {
@@ -350,7 +348,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
int startPos = getArguments().getInt("Position", 0);
// start playing
mPlaybackController.play(startPos);
playbackControllerContainer.getValue().getPlaybackController().play(startPos);
leanbackOverlayFragment.updatePlayState();
}
@@ -358,7 +356,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
private void prepareOverlayFragment() {
leanbackOverlayFragment = (LeanbackOverlayFragment) getChildFragmentManager().findFragmentById(R.id.leanback_fragment);
if (leanbackOverlayFragment != null) {
leanbackOverlayFragment.initFromView(mPlaybackController, this);
leanbackOverlayFragment.initFromView(this);
leanbackOverlayFragment.mediaInfoChanged();
leanbackOverlayFragment.setOnKeyInterceptListener(keyListener);
}
@@ -403,7 +401,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
mPlaybackController.pause();
playbackControllerContainer.getValue().getPlaybackController().pause();
break;
case AudioManager.AUDIOFOCUS_LOSS:
// We don't do anything here on purpose
@@ -424,7 +422,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
switch (rowItem.getBaseRowType()) {
case Chapter:
Long start = rowItem.getChapterInfo().getStartPositionTicks() / 10000;
mPlaybackController.seek(start);
playbackControllerContainer.getValue().getPlaybackController().seek(start);
hidePopupPanel();
break;
}
@@ -445,7 +443,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
leanbackOverlayFragment.hideOverlay();
// also close this if live tv
if (mPlaybackController.isLiveTv()) hide();
if (playbackControllerContainer.getValue().getPlaybackController().isLiveTv()) hide();
} else if (mGuideVisible) {
hideGuide();
} else {
@@ -490,19 +488,19 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
mPlaybackController.play(0);
playbackControllerContainer.getValue().getPlaybackController().play(0);
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mPlaybackController.pause();
playbackControllerContainer.getValue().getPlaybackController().pause();
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
mPlaybackController.playPause();
playbackControllerContainer.getValue().getPlaybackController().playPause();
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD || keyCode == KeyEvent.KEYCODE_BUTTON_R1 || keyCode == KeyEvent.KEYCODE_BUTTON_R2) {
mPlaybackController.fastForward();
playbackControllerContainer.getValue().getPlaybackController().fastForward();
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND || keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_L2) {
mPlaybackController.rewind();
playbackControllerContainer.getValue().getPlaybackController().rewind();
return true;
}
}
@@ -559,7 +557,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
leanbackOverlayFragment.hideOverlay();
// also close this if live tv
if (mPlaybackController.isLiveTv()) hide();
if (playbackControllerContainer.getValue().getPlaybackController().isLiveTv()) hide();
return true;
} else if (mGuideVisible) {
hideGuide();
@@ -567,7 +565,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
}
if (mPlaybackController.isLiveTv() && !mPopupPanelVisible && !mGuideVisible && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if (playbackControllerContainer.getValue().getPlaybackController().isLiveTv() && !mPopupPanelVisible && !mGuideVisible && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if (!leanbackOverlayFragment.isControlsOverlayVisible()) {
leanbackOverlayFragment.setShouldShowOverlay(false);
leanbackOverlayFragment.hideOverlay();
@@ -596,7 +594,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
}
if (mPlaybackController.isLiveTv() && keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BUTTON_Y) {
if (playbackControllerContainer.getValue().getPlaybackController().isLiveTv() && keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BUTTON_Y) {
showGuide();
return true;
}
@@ -612,7 +610,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
// up or down should close panel
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP) {
hidePopupPanel();
if (mPlaybackController.isLiveTv()) hide(); //also close this if live tv
if (playbackControllerContainer.getValue().getPlaybackController().isLiveTv()) hide(); //also close this if live tv
return true;
} else {
return false;
@@ -620,22 +618,22 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
// Control fast forward and rewind if overlay hidden and not showing live TV
if (!mPlaybackController.isLiveTv()) {
if (!playbackControllerContainer.getValue().getPlaybackController().isLiveTv()) {
if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD || keyCode == KeyEvent.KEYCODE_BUTTON_R1 || keyCode == KeyEvent.KEYCODE_BUTTON_R2) {
mPlaybackController.fastForward();
playbackControllerContainer.getValue().getPlaybackController().fastForward();
setFadingEnabled(true);
return true;
}
if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND || keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_L2) {
mPlaybackController.rewind();
playbackControllerContainer.getValue().getPlaybackController().rewind();
setFadingEnabled(true);
return true;
}
}
if (!mIsVisible) {
if (!mPlaybackController.isLiveTv()) {
if (!playbackControllerContainer.getValue().getPlaybackController().isLiveTv()) {
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
setFadingEnabled(true);
return true;
@@ -648,10 +646,10 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER)
&& mPlaybackController.canSeek()) {
&& playbackControllerContainer.getValue().getPlaybackController().canSeek()) {
// if the player is playing and the overlay is hidden, this will pause
// if the player is paused and then 'back' is pressed to hide the overlay, this will play
mPlaybackController.playPause();
playbackControllerContainer.getValue().getPlaybackController().playPause();
return true;
}
@@ -688,12 +686,12 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
public void switchChannel(String id, boolean hideGuide) {
if (Utils.isEmpty(id)) return;
if (mPlaybackController.getCurrentlyPlayingItem().getId().equals(id)) {
if (playbackControllerContainer.getValue().getPlaybackController().getCurrentlyPlayingItem().getId().equals(id)) {
// same channel, just dismiss overlay
if (hideGuide)
hideGuide();
} else {
mPlaybackController.stop();
playbackControllerContainer.getValue().getPlaybackController().stop();
if (hideGuide)
hideGuide();
apiClient.getValue().GetItemAsync(id, KoinJavaComponent.<UserRepository>get(UserRepository.class).getCurrentUser().getValue().getId().toString(), new LifecycleAwareResponse<BaseItemDto>(getLifecycle()) {
@@ -703,8 +701,8 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
List<org.jellyfin.sdk.model.api.BaseItemDto> items = new ArrayList<org.jellyfin.sdk.model.api.BaseItemDto>();
items.add(ModelCompat.asSdk(response));
mPlaybackController.setItems(items);
mPlaybackController.play(0);
playbackControllerContainer.getValue().getPlaybackController().setItems(items);
playbackControllerContainer.getValue().getPlaybackController().play(0);
}
@Override
@@ -728,6 +726,17 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
public void onResume() {
super.onResume();
// Close player when resuming without a valid playback contoller
if (!playbackControllerContainer.getValue().getPlaybackController().hasFragment()) {
if (navigationRepository.getValue().getCanGoBack()) {
navigationRepository.getValue().goBack();
} else {
navigationRepository.getValue().reset(Destinations.INSTANCE.getHome());
}
return;
}
// Hide system bars
WindowCompat.setDecorFitsSystemWindows(requireActivity().getWindow(), false);
WindowCompat.getInsetsController(requireActivity().getWindow(), requireActivity().getWindow().getDecorView()).hide(WindowInsetsCompat.Type.systemBars());
@@ -763,9 +772,9 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
// end playback from here if this fragment belongs to the current session.
// if it doesn't, playback has already been stopped elsewhere, and the references to this have been replaced
if (mPlaybackController != null && mPlaybackController.getFragment() == this) {
if (playbackControllerContainer.getValue().getPlaybackController() != null && playbackControllerContainer.getValue().getPlaybackController().getFragment() == this) {
Timber.d("this fragment belongs to the current session, ending it");
mPlaybackController.endPlayback();
playbackControllerContainer.getValue().getPlaybackController().endPlayback();
}
// Reset display mode back to "no preference"
@@ -801,7 +810,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
hide();
leanbackOverlayFragment.setShouldShowOverlay(false);
leanbackOverlayFragment.hideOverlay();
mPlaybackController.mVideoManager.contractVideo(Utils.convertDpToPixel(requireContext(), 300));
playbackControllerContainer.getValue().getPlaybackController().mVideoManager.contractVideo(Utils.convertDpToPixel(requireContext(), 300));
tvGuideBinding.getRoot().setVisibility(View.VISIBLE);
mGuideVisible = true;
Calendar now = Calendar.getInstance();
@@ -820,7 +829,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
private void hideGuide() {
tvGuideBinding.getRoot().setVisibility(View.GONE);
mPlaybackController.mVideoManager.setVideoFullSize(true);
playbackControllerContainer.getValue().getPlaybackController().mVideoManager.setVideoFullSize(true);
mGuideVisible = false;
}
@@ -893,7 +902,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
Timber.d("*** Display programs pre-execute");
tvGuideBinding.channels.removeAllViews();
tvGuideBinding.programRows.removeAllViews();
mFirstFocusChannelId = mPlaybackController.getCurrentlyPlayingItem().getId().toString();
mFirstFocusChannelId = playbackControllerContainer.getValue().getPlaybackController().getCurrentlyPlayingItem().getId().toString();
if (mCurrentDisplayChannelStartNdx > 0) {
// Show a paging row for channels above
@@ -1227,7 +1236,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
mHandler.postDelayed(() -> {
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) return;
int ndx = getCurrentChapterIndex(mPlaybackController.getCurrentlyPlayingItem(), mPlaybackController.getCurrentPosition() * 10000);
int ndx = getCurrentChapterIndex(playbackControllerContainer.getValue().getPlaybackController().getCurrentlyPlayingItem(), playbackControllerContainer.getValue().getPlaybackController().getCurrentPosition() * 10000);
if (ndx > 0) {
mPopupRowPresenter.setPosition(ndx);
}
@@ -1292,7 +1301,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
if (!getActive()) return;
Utils.showToast(requireContext(), R.string.msg_recording_cancelled);
mPlaybackController.updateTvProgramInfo();
playbackControllerContainer.getValue().getPlaybackController().updateTvProgramInfo();
TvManager.forceReload();
}
@@ -1310,7 +1319,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
if (!getActive()) return;
Utils.showToast(requireContext(), R.string.msg_recording_cancelled);
mPlaybackController.updateTvProgramInfo();
playbackControllerContainer.getValue().getPlaybackController().updateTvProgramInfo();
TvManager.forceReload();
}
@@ -1340,7 +1349,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
if (!getActive()) return;
Utils.showToast(requireContext(), R.string.msg_set_to_record);
mPlaybackController.updateTvProgramInfo();
playbackControllerContainer.getValue().getPlaybackController().updateTvProgramInfo();
TvManager.forceReload();
}
@@ -1358,7 +1367,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
if (!getActive()) return;
Utils.showToast(requireContext(), R.string.msg_set_to_record);
mPlaybackController.updateTvProgramInfo();
playbackControllerContainer.getValue().getPlaybackController().updateTvProgramInfo();
TvManager.forceReload();
}
@@ -1398,7 +1407,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
public void updateDisplay() {
org.jellyfin.sdk.model.api.BaseItemDto current = mPlaybackController.getCurrentlyPlayingItem();
org.jellyfin.sdk.model.api.BaseItemDto current = playbackControllerContainer.getValue().getPlaybackController().getCurrentlyPlayingItem();
if (current != null && getContext() != null) {
leanbackOverlayFragment.mediaInfoChanged();
leanbackOverlayFragment.onFullyInitialized();
@@ -1426,7 +1435,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
binding.itemTitle.setVisibility(View.VISIBLE);
}
if (mPlaybackController.isLiveTv()) {
if (playbackControllerContainer.getValue().getPlaybackController().isLiveTv()) {
prepareChannelAdapter();
} else {
prepareChapterAdapter();
@@ -1435,7 +1444,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
}
private void prepareChapterAdapter() {
org.jellyfin.sdk.model.api.BaseItemDto item = mPlaybackController.getCurrentlyPlayingItem();
org.jellyfin.sdk.model.api.BaseItemDto item = playbackControllerContainer.getValue().getPlaybackController().getCurrentlyPlayingItem();
List<ChapterInfo> chapters = item.getChapters();
if (chapters != null && !chapters.isEmpty()) {

View File

@@ -11,10 +11,9 @@ import org.jellyfin.androidtv.ui.playback.PlaybackController;
import org.jellyfin.androidtv.ui.playback.PlaybackControllerContainer;
import kotlin.Lazy;
import timber.log.Timber;
public class LeanbackOverlayFragment extends PlaybackSupportFragment {
private PlaybackController playbackController;
private CustomPlaybackTransportControlGlue playerGlue;
private VideoPlayerAdapter playerAdapter;
private boolean shouldShowOverlay = true;
@@ -27,14 +26,17 @@ public class LeanbackOverlayFragment extends PlaybackSupportFragment {
setBackgroundType(BG_LIGHT);
PlaybackController playbackController = playbackControllerContainer.getValue().getPlaybackController();
if (playbackController == null) {
Timber.w("PlaybackController is null, skipping initialization.");
return;
}
playerAdapter = new VideoPlayerAdapter(playbackController, this);
playerGlue = new CustomPlaybackTransportControlGlue(getContext(), playerAdapter, playbackController);
playerGlue.setHost(new CustomPlaybackFragmentGlueHost(this));
}
public void initFromView(PlaybackController playbackController, CustomPlaybackOverlayFragment customPlaybackOverlayFragment) {
this.playbackController = playbackController;
public void initFromView(CustomPlaybackOverlayFragment customPlaybackOverlayFragment) {
playerGlue.setInitialPlaybackDrawable();
playerAdapter.setMasterOverlayFragment(customPlaybackOverlayFragment);
}
@@ -69,7 +71,7 @@ public class LeanbackOverlayFragment extends PlaybackSupportFragment {
}
public void mediaInfoChanged() {
org.jellyfin.sdk.model.api.BaseItemDto currentlyPlayingItem = playbackController.getCurrentlyPlayingItem();
org.jellyfin.sdk.model.api.BaseItemDto currentlyPlayingItem = playbackControllerContainer.getValue().getPlaybackController().getCurrentlyPlayingItem();
if (currentlyPlayingItem == null) return;
playerGlue.invalidatePlaybackControls();

View File

@@ -24,6 +24,8 @@ import org.jellyfin.sdk.model.api.SeriesStatus;
import org.koin.java.KoinJavaComponent;
import java.text.NumberFormat;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@@ -315,7 +317,7 @@ public class InfoLayoutHelper {
break;
default:
if (item.getPremiereDate() != null) {
date.setText(DateFormat.getMediumDateFormat(context).format(TimeUtils.getDate(item.getPremiereDate())));
date.setText(item.getPremiereDate().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
layout.addView(date);
addSpacer(context, layout, " ");
} else if (item.getProductionYear() != null && item.getProductionYear() > 0) {
@@ -375,16 +377,17 @@ public class InfoLayoutHelper {
private static void addVideoCodecDetails(Context context, LinearLayout layout, MediaStream stream) {
if (stream != null) {
if (stream.getCodec() != null && stream.getCodec().trim().length() > 0) {
String codec = stream.getCodec().toUpperCase();
addBlockText(context, layout, codec);
addSpacer(context, layout, " ");
}
if (stream.getVideoDoViTitle() != null && stream.getVideoDoViTitle().trim().length() > 0) {
addBlockText(context, layout, "VISION");
addSpacer(context, layout, " ");
} else if (stream.getVideoRangeType() != null && !stream.getVideoRangeType().equals("SDR")) {
addBlockText(context, layout, stream.getVideoRangeType().toUpperCase());
addSpacer(context, layout, " ");
} else if (stream.getCodec() != null && stream.getCodec().trim().length() > 0) {
String codec = stream.getCodec().toUpperCase();
addBlockText(context, layout, codec);
addSpacer(context, layout, " ");
}
}
}

View File

@@ -137,7 +137,7 @@
<string name="lbl_songs">Písně</string>
<string name="lbl_album">Album</string>
<string name="lbl_episodes">Epizody</string>
<string name="lbl_enable_cinema_mode">Povolit režim kina</string>
<string name="lbl_enable_cinema_mode">Kino režim</string>
<string name="lbl_playlists">Seznam skladeb</string>
<string name="msg_items_added">" položky přidány"</string>
<string name="lbl_add_to_queue">Přidat do fronty</string>

View File

@@ -481,4 +481,17 @@
<string name="pref_audio">Audio</string>
<string name="searchable_hint">Pretraži Jellyfin</string>
<string name="episode_name_special">specijalna</string>
<string name="pref_screensaver_inapp_enabled">Koristi čuvar zaslona u aplikaciji</string>
<string name="not_set">Nije postavljeno</string>
<string name="pref_music_rewrite_enable_description">Samo onemogući ovo ako imate poteškoća sa reprodukcijom glazbe</string>
<string name="lbl_album_artists">Izvođači albuma</string>
<string name="pref_music_cat">Glazba</string>
<string name="pref_music_rewrite_disable">Onemogući novu pozadinu glazbe</string>
<string name="disable_ui_mode_warning">Onemogući upozorenje vrste uređaja</string>
<string name="enable_reactive_homepage">Omogući reaktivnu početnu stranicu</string>
<string name="pref_screensaver_inapp_timeout">Pokreni čuvar zaslona nakon</string>
<string name="pref_screensaver">Čuvar zaslona</string>
<string name="pref_screensaver_inapp_enabled_description">Prikaži Jellyfin čuvar zaslona kada je aplikacija otvorena</string>
<string name="lbl_latest_in">Nedavno dodano u %1$s</string>
<string name="because_you_watched">Zato što si gledao %1$s</string>
</resources>

View File

@@ -23,15 +23,15 @@
<string name="lbl_clear_queue">Kuyruğu temizle</string>
<string name="btn_got_it">Anladım</string>
<string name="lbl_subtitle_track">Altyazı parçası seç</string>
<string name="lbl_audio_track">Ses parçası seç</string>
<string name="lbl_audio_track">Ses parçası seçin</string>
<string name="lbl_zoom">Yakınlaşma</string>
<string name="desc_bitstream_ac3">Kapasiteli donanım gerektirir</string>
<string name="desc_bitstream_ac3">Uyumlu donanım gerektirir</string>
<string name="lbl_bitstream_ac3">Bitstream Dolby Digital ses</string>
<string name="lbl_new_premieres">Yeni prömiyerler</string>
<string name="desc_automatic_fav_songs">Favori şarkılardan oluşan otomatik çalma listesi</string>
<string name="msg_item_added">" öğe eklendi"</string>
<string name="lbl_delete">Sil</string>
<string name="lbl_instant_mix">Anında mix</string>
<string name="lbl_instant_mix">Anlık mix</string>
<string name="lbl_goto_now_playing">Şimdi oynatılana git</string>
<string name="lbl_remove_from_queue">Kuyruktan kaldır</string>
<string name="lbl_play_from_here">Buradan oynat</string>
@@ -52,7 +52,7 @@
<string name="lbl_artists">Sanatçılar</string>
<string name="lbl_albums">Albümler</string>
<string name="lbl_programs">Programlar</string>
<string name="lbl_direct_stream_live">Doğrudan Canlı TV akışı</string>
<string name="lbl_direct_stream_live">Doğrudan canlı TV akışı</string>
<string name="msg_error_live_stream">Canlı TV akışı hatası. Yeniden denenecek…</string>
<string name="pref_live_tv_cat">Canlı TV</string>
<string name="lbl_recorded_tv">Kaydedilmiş TV</string>
@@ -79,7 +79,7 @@
<string name="lbl_goto_series">Dizilere git</string>
<string name="lbl_audio_output">Ses çıkışı</string>
<string name="pref_audio_compat">Stereo\'ya düşür</string>
<string name="pref_audio_direct">Direk</string>
<string name="pref_audio_direct">Doğrudan</string>
<string name="msg_cancel_entire_series">TÜM DİZİYİ iptal etmek istediğinize emin misini\?</string>
<string name="lbl_repeat_options">Tekrar seçenekleri</string>
<string name="lbl_only_new_episodes">Sadece yeni bölümler</string>
@@ -106,14 +106,14 @@
<string name="lbl_mark_unplayed">Oynatılmadı olarak işaretle</string>
<string name="lbl_play_first_unwatched">İlk izlenmeyeni oynat</string>
<string name="lbl_search_hint">Arama metni (klavye için seçin)</string>
<string name="sum_tv_queuing">Bir bölümü oynatırken sezonun geri kalanını sıraya ekle</string>
<string name="lbl_tv_queuing">Tüm sonraki bölümleri sıraya ekle</string>
<string name="sum_tv_queuing">Bir bölümü oynatırken sezonun geri kalanını kuyruğa ekle</string>
<string name="lbl_tv_queuing">Tüm sonraki bölümleri kuyruğa ekle</string>
<string name="lbl_empty">Boş</string>
<string name="lbl_no_items">Öge yok</string>
<string name="msg_cannot_play">Bu öğe oynatılamaz</string>
<string name="msg_cannot_play_time">Bu öğe şu anda oynatılamıyor</string>
<string name="lbl_never">Asla</string>
<string name="msg_playback_incompatible">Bu video formatı oynatılamıyor</string>
<string name="msg_playback_incompatible">Bu video biçimi oynatılamıyor</string>
<string name="msg_playback_restricted">Yönetici şu anda bu öğenin oynatılmasını kısıtladı</string>
<string name="msg_playback_not_allowed">Oynatmaya şuan izin verilmiyor</string>
<string name="lbl_loading_elipses">Yükleniyor…</string>
@@ -173,20 +173,20 @@
<string name="lbl_by_name">İsme göre</string>
<string name="lbl_latest">Son eklenen</string>
<string name="msg_not_implemented">" uygulanmadı"</string>
<string name="lbl_search">Arama</string>
<string name="lbl_search">Ara</string>
<string name="lbl_genres">Türler</string>
<string name="lbl_status">%1$s / %2$s</string>
<string name="lbl_suggested">Önerilen</string>
<string name="lbl_by_letter">Harfe göre</string>
<string name="lbl_views">Görüntüleme</string>
<string name="lbl_on_now">Yayında</string>
<string name="lbl_next_up">Sıradaki bölüm</string>
<string name="lbl_next_up">Sıradaki bölümler</string>
<string name="lbl_continue_watching">İzlemeye devam et</string>
<string name="lbl_settings">Tercihler</string>
<string name="chapters">Bölümler</string>
<string name="pref_authentication_cat">Doğrulama</string>
<string name="settings_title">Tercihler</string>
<string name="pref_max_bitrate_title">Maksimum Akış Bit Hızı</string>
<string name="pref_max_bitrate_title">Maksimum akış bit hızı</string>
<string name="pref_playback">Oynatma</string>
<string name="byletter_letters">ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ</string>
<string name="btn_cancel">İptal</string>
@@ -237,7 +237,7 @@
<string name="pref_libvlc_audio_delay_title">libVLC için varsayılan ses gecikmesi</string>
<string name="watch_now">Şimdi izle</string>
<string name="pref_next_up_timeout_title">Sıradaki zamanlayıcı süresi</string>
<string name="pref_next_up_timeout_summary">Bir sonraki öğeyi çalmadan önce bekleme süresi</string>
<string name="pref_next_up_timeout_summary">Bir sonraki ögeyi çalmadan önce bekleme süresi</string>
<string name="pref_media_player">Tercih edilen medya oynatıcısı</string>
<string name="pref_about_title">Hakkında</string>
<string name="pref_video_player_choose">Her zaman sor</string>
@@ -265,8 +265,8 @@
<string name="lbl_next_episode">Sonraki bölüm</string>
<string name="lbl_previous_episode">Önceki bölüm</string>
<string name="lbl_more_like_this">Bunu gibi daha fazlası</string>
<string name="lbl_resume_from">%1$s\'den devam et</string>
<string name="lbl_resume_preroll">Ön gösterimi devam ettir</string>
<string name="lbl_resume_from">Sürdür: %1$s</string>
<string name="lbl_resume_preroll">Ön gösterimi sürdür</string>
<string name="lbl_guide_option_number">Kanal numarası</string>
<string name="lbl_guide_option_played">Son oynatılan</string>
<string name="lbl_show_indicators">Göstergeleri göster:</string>
@@ -304,7 +304,7 @@
<string name="home_section_i">Ana Ekran bölümü %1$d</string>
<string name="home_section_none">Hiçbiri</string>
<string name="home_section_livetv">Canlı TV</string>
<string name="home_section_next_up">Sıradaki</string>
<string name="home_section_next_up">Sıradaki bölümler</string>
<string name="home_section_active_recordings">Etkin kayıtlar</string>
<string name="home_section_resume_audio">Dinlemeye devam et</string>
<string name="home_section_resume">İzlemeye devam et</string>
@@ -315,7 +315,7 @@
<string name="home_prefs">Ana Ekran</string>
<string name="license_license">Lisans</string>
<string name="license_author">Yazar</string>
<string name="license_repository">Depolama yeri</string>
<string name="license_repository">Depo</string>
<string name="license_website">Web sitesi</string>
<string name="license_version">Sürüm</string>
<string name="license_description">ıklama</string>
@@ -420,14 +420,14 @@
<string name="pref_theme">Tema</string>
<string name="pref_customization">Özelleştirme</string>
<string name="pref_subtitles_background_title">Arka planı etkinleştir</string>
<string name="server_issue_missing_version">Sunucu versiyonu algılanamadı</string>
<string name="server_issue_missing_version">Sunucu sürümü algılanamadı</string>
<string name="server_issue_outdated_version">Sunucu desteklenmeyen %1$s sürümünü kullanıyor. Lütfen %2$s veya daha yeni bir sürüme güncelleyin</string>
<string name="pref_behavior">Davranış</string>
<string name="server_issue_unsupported_version">Sunucu desteklenmeyen %1$s sürümünü kullanıyor</string>
<string name="action_use_password">Bir parola kullanın</string>
<string name="input_password">Parola</string>
<string name="pref_audio_night_mode">Gece ses modu (deneysel)</string>
<string name="because_you_watched">%1$s izlediğin için</string>
<string name="because_you_watched">%1$s izlediğiniz için</string>
<string name="image_size_xlarge">Ekstra Büyük</string>
<string name="sort_accounts_by">Hesapları sırala:</string>
<string name="lbl_quality_profile">Kalite profili</string>
@@ -438,7 +438,7 @@
<string name="pref_customization_description">Tema, ana ekran, yeniden eşleştirme</string>
<string name="pref_video">Video</string>
<string name="action_use_quickconnect">Hızlı Bağlan\'ı Kullan</string>
<string name="app_notification_uimode_invalid">Bu uygulama televizyonlar için optimize edilmiştir. Mobil uygulamamızı diğer cihazlarda kullanmanızı öneririz.</string>
<string name="app_notification_uimode_invalid">Bu uygulama televizyonlar için optimize edilmiştir. Diğer cihazlarda mobil uygulamamızı kullanmanızı öneririz.</string>
<string name="pref_crash_reports_disabled">Çökme raporları gönderilmeyecektir</string>
<string name="pref_crash_report_logs">Günlükleri dahil et</string>
<string name="pref_crash_report_logs_disabled">Günlükler çökme raporlarına dahil edilmeyecektir</string>
@@ -459,7 +459,7 @@
<string name="pref_music_rewrite_disable">Yeni müzik arka ucunu devre dışı bırak</string>
<string name="disable_ui_mode_warning">Cihaz türü uyarısını devre dışı bırak</string>
<string name="pref_browsing">Gözatma</string>
<string name="pref_crash_reports">Sunucuya çökme raporları gönderme</string>
<string name="pref_crash_reports">Sunucuya çökme raporlarını gönder</string>
<plurals name="tracks">
<item quantity="one">%1$s parça</item>
<item quantity="other">%1$s parça</item>

View File

@@ -1,30 +1,30 @@
[versions]
aboutlibraries = "10.9.1"
aboutlibraries = "10.9.2"
acra = "5.11.3"
android-desugar = "2.0.3"
android-gradle = "8.1.1"
androidx-activity = "1.8.0"
android-desugar = "2.0.4"
android-gradle = "8.2.0"
androidx-activity = "1.8.2"
androidx-appcompat = "1.6.1"
androidx-cardview = "1.0.0"
androidx-compose-compiler = "1.5.3"
androidx-compose-compiler = "1.5.6"
androidx-compose-foundation = "1.5.4"
androidx-compose-material = "1.5.4"
androidx-compose-ui = "1.5.4"
androidx-constraintlayout = "2.1.4"
androidx-core = "1.12.0"
androidx-fragment = "1.6.1"
androidx-fragment = "1.6.2"
androidx-leanback = "1.1.0-rc01"
androidx-lifecycle = "2.6.2"
androidx-media3 = "1.1.1"
androidx-media3 = "1.2.0"
androidx-preference = "1.2.1"
androidx-recyclerview = "1.3.2"
androidx-startup = "1.1.1"
androidx-tvprovider = "1.1.0-alpha01"
androidx-window = "1.1.0"
androidx-work = "2.8.1"
blurhash = "0.1.0"
coil = "2.4.0"
detekt = "1.23.1"
androidx-window = "1.2.0"
androidx-work = "2.9.0"
blurhash = "0.2.0"
coil = "2.5.0"
detekt = "1.23.4"
exoplayer = "2.19.1"
gson = "2.8.9"
jellyfin-apiclient = "v0.7.10"
@@ -33,10 +33,10 @@ jellyfin-sdk = "1.4.6"
junit = "4.13.2"
koin = "3.5.0"
koin-compose = "3.5.0"
kotest = "5.7.2"
kotlin = "1.9.10"
kotest = "5.8.0"
kotlin = "1.9.21"
kotlinx-coroutines = "1.7.3"
kotlinx-serialization = "1.6.0"
kotlinx-serialization = "1.6.2"
leakcanary = "2.12"
libvlc = "3.5.1"
markwon = "4.6.2"
@@ -84,7 +84,7 @@ androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version
androidx-startup = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup" }
androidx-tvprovider = { module = "androidx.tvprovider:tvprovider", version.ref = "androidx-tvprovider" }
androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" }
androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" }
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "androidx-work" }
# Dependency Injection
koin-android-compat = { module = "io.insert-koin:koin-android-compat", version.ref = "koin" }

Binary file not shown.

View File

@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME