mirror of
https://github.com/jellyfin/jellyfin-androidtv.git
synced 2024-11-27 08:00:28 +00:00
Remove custom subtitle renderer
This commit is contained in:
parent
0f38bd1691
commit
be06693978
@ -9,9 +9,6 @@ import android.media.AudioManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -25,7 +22,6 @@ import android.widget.TextView;
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@ -45,7 +41,6 @@ import org.jellyfin.androidtv.data.repository.CustomMessageRepository;
|
||||
import org.jellyfin.androidtv.data.service.BackgroundService;
|
||||
import org.jellyfin.androidtv.databinding.OverlayTvGuideBinding;
|
||||
import org.jellyfin.androidtv.databinding.VlcPlayerInterfaceBinding;
|
||||
import org.jellyfin.androidtv.preference.UserPreferences;
|
||||
import org.jellyfin.androidtv.ui.GuideChannelHeader;
|
||||
import org.jellyfin.androidtv.ui.GuidePagingButton;
|
||||
import org.jellyfin.androidtv.ui.HorizontalScrollViewListener;
|
||||
@ -67,7 +62,6 @@ import org.jellyfin.androidtv.ui.presentation.CardPresenter;
|
||||
import org.jellyfin.androidtv.ui.presentation.ChannelCardPresenter;
|
||||
import org.jellyfin.androidtv.ui.presentation.MutableObjectAdapter;
|
||||
import org.jellyfin.androidtv.ui.presentation.PositionableListRowPresenter;
|
||||
import org.jellyfin.androidtv.ui.shared.PaddedLineBackgroundSpan;
|
||||
import org.jellyfin.androidtv.util.CoroutineUtils;
|
||||
import org.jellyfin.androidtv.util.DateTimeExtensionsKt;
|
||||
import org.jellyfin.androidtv.util.ImageHelper;
|
||||
@ -77,12 +71,9 @@ import org.jellyfin.androidtv.util.TimeUtils;
|
||||
import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.EmptyLifecycleAwareResponse;
|
||||
import org.jellyfin.androidtv.util.sdk.BaseItemExtensionsKt;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackEvent;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackInfo;
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto;
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind;
|
||||
import org.jellyfin.sdk.model.api.ChapterInfo;
|
||||
import org.koin.java.KoinJavaComponent;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
@ -133,18 +124,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
|
||||
|
||||
private LeanbackOverlayFragment leanbackOverlayFragment;
|
||||
|
||||
// Subtitle fields
|
||||
private static final int SUBTITLE_PADDING = 20;
|
||||
private static final long SUBTITLE_RENDER_INTERVAL_MS = 50;
|
||||
private SubtitleTrackInfo subtitleTrackInfo;
|
||||
private int currentSubtitleIndex = 0;
|
||||
private long lastSubtitlePositionMs = 0;
|
||||
private final UserPreferences userPreferences = KoinJavaComponent.<UserPreferences>get(UserPreferences.class);
|
||||
private final int subtitlesSize = userPreferences.get(UserPreferences.Companion.getSubtitlesSize());
|
||||
private final boolean subtitlesBackgroundEnabled = userPreferences.get(UserPreferences.Companion.getSubtitlesBackgroundEnabled());
|
||||
private final int subtitlesPosition = userPreferences.get(UserPreferences.Companion.getSubtitlePosition());
|
||||
private final int subtitlesStrokeWidth = userPreferences.get(UserPreferences.Companion.getSubtitleStrokeSize());
|
||||
|
||||
private final Lazy<org.jellyfin.sdk.api.client.ApiClient> api = inject(org.jellyfin.sdk.api.client.ApiClient.class);
|
||||
private final Lazy<MediaManager> mediaManager = inject(MediaManager.class);
|
||||
private final Lazy<VideoQueueManager> videoQueueManager = inject(VideoQueueManager.class);
|
||||
@ -259,26 +238,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
|
||||
|
||||
prepareOverlayFragment();
|
||||
|
||||
//manual subtitles
|
||||
// This configuration is required for the PaddedLineBackgroundSpan to work
|
||||
binding.subtitlesText.setShadowLayer(SUBTITLE_PADDING, 0, 0, Color.TRANSPARENT);
|
||||
binding.subtitlesText.setPadding(SUBTITLE_PADDING, 0, SUBTITLE_PADDING, 0);
|
||||
|
||||
// Subtitles font size configuration
|
||||
binding.subtitlesText.setTextSize(subtitlesSize);
|
||||
|
||||
// Subtitles font position (margin bottom)
|
||||
if (subtitlesPosition > 0) {
|
||||
ViewGroup.MarginLayoutParams currentLayoutParams = (ViewGroup.MarginLayoutParams) binding.subtitlesText.getLayoutParams();
|
||||
currentLayoutParams.bottomMargin = (8 + Utils.convertDpToPixel(requireContext(), subtitlesPosition));
|
||||
binding.subtitlesText.setLayoutParams(currentLayoutParams);
|
||||
}
|
||||
|
||||
// Subtitles stroke width
|
||||
if (subtitlesStrokeWidth > 0 && !subtitlesBackgroundEnabled) {
|
||||
binding.subtitlesText.setStrokeWidth(subtitlesStrokeWidth);
|
||||
}
|
||||
|
||||
//pre-load animations
|
||||
fadeOut = AnimationUtils.loadAnimation(requireContext(), androidx.leanback.R.anim.abc_fade_out);
|
||||
fadeOut.setAnimationListener(hideAnimationListener);
|
||||
@ -1313,137 +1272,6 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
|
||||
navigationRepository.getValue().navigate(Destinations.INSTANCE.nextUp(id), true);
|
||||
}
|
||||
|
||||
public void addManualSubtitles(@Nullable SubtitleTrackInfo info) {
|
||||
subtitleTrackInfo = info;
|
||||
currentSubtitleIndex = -1;
|
||||
lastSubtitlePositionMs = 0;
|
||||
clearSubtitles();
|
||||
}
|
||||
|
||||
public void showSubLoadingMsg(final boolean show) {
|
||||
if (show) {
|
||||
renderSubtitles(requireContext().getString(R.string.msg_subtitles_loading));
|
||||
} else {
|
||||
clearSubtitles();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSubtitles(long positionMs) {
|
||||
int iterCount = 1;
|
||||
final long positionTicks = positionMs * 10000;
|
||||
final long lastPositionTicks = lastSubtitlePositionMs * 10000;
|
||||
|
||||
if (subtitleTrackInfo == null
|
||||
|| subtitleTrackInfo.getTrackEvents() == null
|
||||
|| subtitleTrackInfo.getTrackEvents().size() < 1
|
||||
|| currentSubtitleIndex >= subtitleTrackInfo.getTrackEvents().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (positionTicks < subtitleTrackInfo.getTrackEvents().get(0).getStartPositionTicks())
|
||||
return;
|
||||
|
||||
// Skip rendering if the interval ms have not passed since last render
|
||||
if (lastSubtitlePositionMs > 0
|
||||
&& Math.abs(lastSubtitlePositionMs - positionMs) < SUBTITLE_RENDER_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user has skipped back, reset the subtitle index
|
||||
if (lastSubtitlePositionMs > positionMs) {
|
||||
currentSubtitleIndex = -1;
|
||||
clearSubtitles();
|
||||
}
|
||||
|
||||
if (currentSubtitleIndex == -1)
|
||||
Timber.d("subtitle track events size %s", subtitleTrackInfo.getTrackEvents().size());
|
||||
|
||||
// Find the next subtitle event that should be rendered
|
||||
for (int tmpSubtitleIndex = currentSubtitleIndex == -1 ? 0 : currentSubtitleIndex; tmpSubtitleIndex < subtitleTrackInfo.getTrackEvents().size(); tmpSubtitleIndex++) {
|
||||
SubtitleTrackEvent trackEvent = subtitleTrackInfo.getTrackEvents().get(tmpSubtitleIndex);
|
||||
|
||||
if (positionTicks >= trackEvent.getStartPositionTicks()
|
||||
&& positionTicks < trackEvent.getEndPositionTicks()) {
|
||||
// This subtitle event should be displayed now
|
||||
// use lastPositionTicks to ensure it is only rendered once
|
||||
|
||||
if (lastPositionTicks < trackEvent.getStartPositionTicks() || lastPositionTicks >= trackEvent.getEndPositionTicks()) {
|
||||
Timber.d("rendering subtitle event: %s (pos %s start %s end %s)", tmpSubtitleIndex, positionMs, trackEvent.getStartPositionTicks() / 10000, trackEvent.getEndPositionTicks() / 10000);
|
||||
renderSubtitles(trackEvent.getText());
|
||||
}
|
||||
|
||||
currentSubtitleIndex = tmpSubtitleIndex;
|
||||
lastSubtitlePositionMs = positionMs;
|
||||
// rendering should happen on the 2nd iteration
|
||||
if (iterCount > 2)
|
||||
Timber.d("subtitles handled in %s iterations", iterCount);
|
||||
return;
|
||||
} else if (tmpSubtitleIndex < subtitleTrackInfo.getTrackEvents().size() - 1) {
|
||||
SubtitleTrackEvent nextTrackEvent = subtitleTrackInfo.getTrackEvents().get(tmpSubtitleIndex + 1);
|
||||
|
||||
if (positionTicks >= trackEvent.getEndPositionTicks() && positionTicks < nextTrackEvent.getStartPositionTicks()) {
|
||||
// clear the subtitles if between events
|
||||
// use lastPositionTicks to ensure it is only cleared once
|
||||
|
||||
if (currentSubtitleIndex > -1 && !(lastPositionTicks >= trackEvent.getEndPositionTicks() && lastPositionTicks < nextTrackEvent.getStartPositionTicks())) {
|
||||
Timber.d("clearing subtitle event: %s (pos %s - event end %s)", tmpSubtitleIndex, positionMs, trackEvent.getEndPositionTicks() / 10000);
|
||||
clearSubtitles();
|
||||
}
|
||||
|
||||
// set currentSubtitleIndex in case it was -1
|
||||
currentSubtitleIndex = tmpSubtitleIndex;
|
||||
lastSubtitlePositionMs = positionMs;
|
||||
if (iterCount > 1)
|
||||
Timber.d("subtitles handled in %s iterations", iterCount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
iterCount++;
|
||||
}
|
||||
// handles clearing the last event
|
||||
if (iterCount > 1)
|
||||
Timber.d("subtitles handled in %s iterations", iterCount);
|
||||
clearSubtitles();
|
||||
}
|
||||
|
||||
private void clearSubtitles() {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
binding.subtitlesText.setVisibility(View.INVISIBLE);
|
||||
binding.subtitlesText.setText(null);
|
||||
});
|
||||
}
|
||||
|
||||
private void renderSubtitles(@Nullable final String text) {
|
||||
if (text == null || text.length() == 0) {
|
||||
clearSubtitles();
|
||||
return;
|
||||
}
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
final String htmlText = text
|
||||
// Encode whitespace as html entities
|
||||
.replaceAll("\\r\\n", "<br>")
|
||||
.replaceAll("\\n", "<br>")
|
||||
.replaceAll("\\\\h", " ")
|
||||
// Remove SSA tags
|
||||
.replaceAll("\\{\\\\.*?\\}", "");
|
||||
|
||||
final SpannableString span = new SpannableString(TextUtilsKt.toHtmlSpanned(htmlText));
|
||||
if (subtitlesBackgroundEnabled) {
|
||||
// Disable the text outlining when the background is enabled
|
||||
binding.subtitlesText.setStrokeWidth(0.0f);
|
||||
|
||||
// get the alignment gravity of the TextView
|
||||
// extract the absolute horizontal gravity so the span can draw its background aligned
|
||||
int gravity = binding.subtitlesText.getGravity();
|
||||
int horizontalGravity = Gravity.getAbsoluteGravity(gravity, binding.subtitlesText.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
span.setSpan(new PaddedLineBackgroundSpan(ContextCompat.getColor(requireContext(), R.color.black_opaque), SUBTITLE_PADDING, horizontalGravity), 0, span.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
binding.subtitlesText.setText(span);
|
||||
binding.subtitlesText.setVisibility(View.VISIBLE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
@ -28,13 +28,11 @@ import org.jellyfin.androidtv.util.Utils;
|
||||
import org.jellyfin.androidtv.util.apiclient.ReportingHelper;
|
||||
import org.jellyfin.androidtv.util.apiclient.StreamHelper;
|
||||
import org.jellyfin.androidtv.util.profile.ExoPlayerProfile;
|
||||
import org.jellyfin.androidtv.util.sdk.ModelUtils;
|
||||
import org.jellyfin.androidtv.util.sdk.compat.JavaCompat;
|
||||
import org.jellyfin.apiclient.interaction.ApiClient;
|
||||
import org.jellyfin.apiclient.interaction.Response;
|
||||
import org.jellyfin.apiclient.model.dlna.DeviceProfile;
|
||||
import org.jellyfin.apiclient.model.dlna.SubtitleDeliveryMethod;
|
||||
import org.jellyfin.apiclient.model.mediainfo.SubtitleTrackInfo;
|
||||
import org.jellyfin.apiclient.model.session.PlayMethod;
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto;
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind;
|
||||
@ -753,9 +751,6 @@ public class PlaybackController implements PlaybackControllerNotifiable {
|
||||
refreshCurrentPosition();
|
||||
Timber.d("Setting subtitle index to: %d", index);
|
||||
|
||||
// clear subtitles first
|
||||
if (mFragment != null) mFragment.addManualSubtitles(null);
|
||||
mVideoManager.disableSubs();
|
||||
// clear the default in case there's an error loading the subtitles
|
||||
mDefaultSubIndex = -1;
|
||||
|
||||
@ -796,63 +791,16 @@ public class PlaybackController implements PlaybackControllerNotifiable {
|
||||
// when burnt-in subtitles are selected, mCurrentOptions SubtitleStreamIndex is set in startItem() as soon as playback starts
|
||||
// otherwise mCurrentOptions SubtitleStreamIndex is kept null until now so we knew subtitles needed to be enabled but weren't already
|
||||
|
||||
switch (streamInfo.getDeliveryMethod()) {
|
||||
case Embed:
|
||||
if (!mVideoManager.setExoPlayerTrack(index, MediaStreamType.SUBTITLE, getCurrentlyPlayingItem().getMediaStreams())) {
|
||||
if (streamInfo.getDeliveryMethod() == SubtitleDeliveryMethod.Embed) {
|
||||
if (!mVideoManager.setExoPlayerTrack(index, MediaStreamType.SUBTITLE, getCurrentlyPlayingItem().getMediaStreams())) {
|
||||
// error selecting internal subs
|
||||
if (mFragment != null)
|
||||
Utils.showToast(mFragment.getContext(), mFragment.getString(R.string.msg_unable_load_subs));
|
||||
} else {
|
||||
mCurrentOptions.setSubtitleStreamIndex(index);
|
||||
mDefaultSubIndex = index;
|
||||
}
|
||||
break;
|
||||
case External:
|
||||
if (mFragment != null) mFragment.showSubLoadingMsg(true);
|
||||
|
||||
stream = ModelUtils.withDelivery(
|
||||
stream,
|
||||
org.jellyfin.sdk.model.api.SubtitleDeliveryMethod.EXTERNAL,
|
||||
String.format(
|
||||
"%1$s/Videos/%2$s/%3$s/Subtitles/%4$s/0/Stream.JSON",
|
||||
apiClient.getValue().getApiUrl(),
|
||||
mCurrentStreamInfo.getItemId(),
|
||||
mCurrentStreamInfo.getMediaSourceId(),
|
||||
String.valueOf(stream.getIndex())
|
||||
)
|
||||
);
|
||||
apiClient.getValue().getSubtitles(stream.getDeliveryUrl(), new Response<SubtitleTrackInfo>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(final SubtitleTrackInfo info) {
|
||||
|
||||
if (info != null) {
|
||||
Timber.d("Adding json subtitle track to player");
|
||||
if (mFragment != null) mFragment.addManualSubtitles(info);
|
||||
mCurrentOptions.setSubtitleStreamIndex(index);
|
||||
mDefaultSubIndex = index;
|
||||
} else {
|
||||
Timber.e("Empty subtitle result");
|
||||
if (mFragment != null) {
|
||||
Utils.showToast(mFragment.getContext(), mFragment.getString(R.string.msg_unable_load_subs));
|
||||
mFragment.showSubLoadingMsg(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
Timber.e(ex, "Error downloading subtitles");
|
||||
if (mFragment != null) {
|
||||
Utils.showToast(mFragment.getContext(), mFragment.getString(R.string.msg_unable_load_subs));
|
||||
mFragment.showSubLoadingMsg(false);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
break;
|
||||
case Hls:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1279,9 +1227,6 @@ public class PlaybackController implements PlaybackControllerNotifiable {
|
||||
stopSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
if (mFragment != null && finishedInitialSeek)
|
||||
mFragment.updateSubtitles(mCurrentPosition);
|
||||
}
|
||||
if (mFragment != null)
|
||||
mFragment.setCurrentTime(mCurrentPosition);
|
||||
|
@ -37,12 +37,9 @@ import androidx.media3.extractor.ts.TsExtractor;
|
||||
import androidx.media3.ui.AspectRatioFrameLayout;
|
||||
import androidx.media3.ui.PlayerView;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.jellyfin.androidtv.R;
|
||||
import org.jellyfin.androidtv.preference.UserPreferences;
|
||||
import org.jellyfin.sdk.model.api.MediaStream;
|
||||
import org.jellyfin.sdk.model.api.MediaStreamType;
|
||||
import org.koin.java.KoinJavaComponent;
|
||||
|
||||
import java.util.List;
|
||||
@ -84,11 +81,6 @@ public class VideoManager {
|
||||
// Volume normalisation (audio night mode).
|
||||
if (nightModeEnabled) enableAudioNightMode(mExoPlayer.getAudioSessionId());
|
||||
|
||||
mExoPlayer.setTrackSelectionParameters(mExoPlayer.getTrackSelectionParameters()
|
||||
.buildUpon()
|
||||
.setDisabledTrackTypes(ImmutableSet.of(C.TRACK_TYPE_TEXT))
|
||||
.build());
|
||||
|
||||
mExoPlayerView = view.findViewById(R.id.exoPlayerView);
|
||||
mExoPlayerView.setPlayer(mExoPlayer);
|
||||
mExoPlayer.addListener(new Player.Listener() {
|
||||
@ -268,7 +260,6 @@ public class VideoManager {
|
||||
public void stopPlayback() {
|
||||
if (mExoPlayer != null) {
|
||||
mExoPlayer.stop();
|
||||
disableSubs();
|
||||
|
||||
mExoPlayer.setTrackSelectionParameters(mExoPlayer.getTrackSelectionParameters()
|
||||
.buildUpon()
|
||||
@ -312,16 +303,6 @@ public class VideoManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void disableSubs() {
|
||||
if (!isInitialized())
|
||||
return;
|
||||
|
||||
mExoPlayer.setTrackSelectionParameters(mExoPlayer.getTrackSelectionParameters()
|
||||
.buildUpon()
|
||||
.setDisabledTrackTypes(ImmutableSet.of(C.TRACK_TYPE_TEXT))
|
||||
.build());
|
||||
}
|
||||
|
||||
private int offsetStreamIndex(int index, boolean adjustByAdding, boolean indexStartsAtOne, @Nullable List<org.jellyfin.sdk.model.api.MediaStream> allStreams) {
|
||||
if (index < 0 || allStreams == null)
|
||||
return -1;
|
||||
@ -399,13 +380,9 @@ public class VideoManager {
|
||||
int chosenTrackType = streamType == org.jellyfin.sdk.model.api.MediaStreamType.SUBTITLE ? C.TRACK_TYPE_TEXT : C.TRACK_TYPE_AUDIO;
|
||||
|
||||
// Make sure the index is present
|
||||
Optional<MediaStream> candidateOptional = allStreams.stream().filter(stream -> stream.getIndex() == index).findFirst();
|
||||
Optional<MediaStream> candidateOptional = allStreams.stream().filter(stream -> stream.getIndex() == index && !stream.isExternal() && stream.getType() == streamType).findFirst();
|
||||
if (!candidateOptional.isPresent()) return false;
|
||||
|
||||
org.jellyfin.sdk.model.api.MediaStream candidate = candidateOptional.get();
|
||||
if (candidate.isExternal() || candidate.getType() != streamType)
|
||||
return false;
|
||||
|
||||
int exoTrackID = offsetStreamIndex(index, false, true, allStreams);
|
||||
if (exoTrackID < 0)
|
||||
return false;
|
||||
@ -471,8 +448,6 @@ public class VideoManager {
|
||||
try {
|
||||
TrackSelectionParameters.Builder mExoPlayerSelectionParams = mExoPlayer.getTrackSelectionParameters().buildUpon();
|
||||
mExoPlayerSelectionParams.setOverrideForType(new TrackSelectionOverride(matchedGroup, 0));
|
||||
if (streamType == MediaStreamType.SUBTITLE)
|
||||
mExoPlayerSelectionParams.setDisabledTrackTypes(ImmutableSet.of(C.TRACK_TYPE_NONE));
|
||||
mExoPlayer.setTrackSelectionParameters(mExoPlayerSelectionParams.build());
|
||||
} catch (Exception e) {
|
||||
Timber.d("Error setting track selection");
|
||||
|
@ -1,51 +0,0 @@
|
||||
package org.jellyfin.androidtv.ui.shared
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.jellyfin.androidtv.R
|
||||
import org.jellyfin.androidtv.preference.UserPreferences
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
|
||||
class StrokeTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : AppCompatTextView(context, attrs, defStyleAttr), KoinComponent {
|
||||
var strokeWidth = 0.0f
|
||||
private var isDrawing: Boolean = false
|
||||
private val subtitlesTextColor = get<UserPreferences>()[UserPreferences.subtitlesTextColor]
|
||||
|
||||
init {
|
||||
val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.StrokeTextView)
|
||||
strokeWidth = styledAttrs.getFloat(R.styleable.StrokeTextView_strokeWidth, 0.0f)
|
||||
styledAttrs.recycle()
|
||||
}
|
||||
|
||||
override fun invalidate() {
|
||||
// To prevent infinite call of onDraw because setTextColor calls invalidate()
|
||||
if (isDrawing) return
|
||||
super.invalidate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
if (strokeWidth <= 0) return super.onDraw(canvas)
|
||||
isDrawing = true
|
||||
paint.isAntiAlias = true
|
||||
paint.strokeWidth = strokeWidth
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeJoin = Paint.Join.ROUND
|
||||
setTextColor(ContextCompat.getColor(context, R.color.black))
|
||||
super.onDraw(canvas)
|
||||
|
||||
paint.style = Paint.Style.FILL
|
||||
setTextColor(ColorStateList.valueOf(subtitlesTextColor.toInt()))
|
||||
super.onDraw(canvas)
|
||||
isDrawing = false
|
||||
}
|
||||
}
|
@ -216,8 +216,8 @@ class ExoPlayerProfile(
|
||||
}.toTypedArray()
|
||||
|
||||
subtitleProfiles = arrayOf(
|
||||
subtitleProfile(Codec.Subtitle.SRT, SubtitleDeliveryMethod.External),
|
||||
subtitleProfile(Codec.Subtitle.SUBRIP, SubtitleDeliveryMethod.External),
|
||||
subtitleProfile(Codec.Subtitle.SRT, SubtitleDeliveryMethod.Embed),
|
||||
subtitleProfile(Codec.Subtitle.SUBRIP, SubtitleDeliveryMethod.Embed),
|
||||
subtitleProfile(Codec.Subtitle.ASS, SubtitleDeliveryMethod.Encode),
|
||||
subtitleProfile(Codec.Subtitle.SSA, SubtitleDeliveryMethod.Encode),
|
||||
subtitleProfile(Codec.Subtitle.PGS, SubtitleDeliveryMethod.Embed),
|
||||
|
@ -38,20 +38,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<org.jellyfin.androidtv.ui.shared.StrokeTextView
|
||||
android:id="@+id/subtitles_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginHorizontal="120dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="28sp"
|
||||
android:textDirection="ltr"
|
||||
app:strokeWidth="5.0"
|
||||
tools:text="Subtitles" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
|
Loading…
Reference in New Issue
Block a user