Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1873497fec | ||
|
|
32849f5279 | ||
|
|
0c1aac96d7 | ||
|
|
7fefb735d9 | ||
|
|
00c59f8e58 | ||
|
|
c87121502c | ||
|
|
636facc8b2 | ||
|
|
98ac4d3eda | ||
|
|
d3cd69530a | ||
|
|
9937ad3ddd | ||
|
|
9a6fc333a1 | ||
|
|
5811b99b35 | ||
|
|
72c97635e3 | ||
|
|
afddc96789 | ||
|
|
a8ef52bca9 | ||
|
|
a0c81df329 | ||
|
|
c4db326da6 | ||
|
|
7d53fee677 | ||
|
|
fa10019f8b | ||
|
|
b4fbd82c21 | ||
|
|
db6c23877f | ||
|
|
f8fd9765ac | ||
|
|
8b5ad52fb9 | ||
|
|
90f2623c9a | ||
|
|
9d2dacdac3 | ||
|
|
174ed2e281 | ||
|
|
8a0b97619b | ||
|
|
3d2c2c7363 | ||
|
|
b2fb409605 | ||
|
|
f14b8ed97b | ||
|
|
92e91ab217 | ||
|
|
4dd363b0cf | ||
|
|
a083dfcc04 | ||
|
|
d3dba03ee0 | ||
|
|
56d6661906 | ||
|
|
2fd2efa581 | ||
|
|
d866b1bce1 | ||
|
|
238a9d27fd | ||
|
|
004b39e89b | ||
|
|
d9eb849038 |
6
.github/workflows/app-build.yaml
vendored
6
.github/workflows/app-build.yaml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/app-lint.yaml
vendored
6
.github/workflows/app-lint.yaml
vendored
@@ -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: .
|
||||
|
||||
4
.github/workflows/app-publish.yaml
vendored
4
.github/workflows/app-publish.yaml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/app-test.yaml
vendored
4
.github/workflows/app-test.yaml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/repo-stale.yaml
vendored
2
.github/workflows/repo-stale.yaml
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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">Açı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>
|
||||
|
||||
@@ -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" }
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user