persist state between the sessions

This commit is contained in:
Yuriy Liskov
2020-10-16 01:18:12 +03:00
parent 755b285f4f
commit 3a46080d00
11 changed files with 167 additions and 87 deletions

View File

@ -25,8 +25,7 @@ public class MainPlayerEventBridge implements PlayerEventListener {
private final ArrayList<PlayerHandlerEventListener> mEventListeners;
@SuppressLint("StaticFieldLeak")
private static MainPlayerEventBridge sInstance;
private PlaybackController mController;
private Activity mMainActivity;
private boolean mInitDone;
public MainPlayerEventBridge() {
mEventListeners = new ArrayList<>();
@ -63,16 +62,12 @@ public class MainPlayerEventBridge implements PlayerEventListener {
Fragment fragment = (Fragment) controller;
Activity mainActivity = fragment.getActivity();
if (mMainActivity != mainActivity) {
mMainActivity = mainActivity;
process(listener -> listener.onActivity(mainActivity));
process(listener -> listener.onController(controller));
process(listener -> listener.onActivity(mainActivity));
}
if (mController != controller) {
mController = controller;
process(listener -> listener.onController(controller));
if (!mInitDone) {
mInitDone = true;
process(PlayerHandlerEventListener::onInitDone);
}
}
}

View File

@ -20,6 +20,11 @@ public abstract class PlayerEventListenerHelper implements PlayerHandlerEventLis
mActivity = activity;
}
@Override
public void onInitDone() {
// NOP
}
@Override
public void openVideo(Video item) {
// NOP

View File

@ -10,4 +10,5 @@ public interface PlayerHandlerEventListener extends PlayerUiEventListener, Playe
void openVideo(Video item);
void onController(PlaybackController controller);
void onActivity(Activity activity);
void onInitDone();
}

View File

@ -1,6 +1,5 @@
package com.liskovsoft.smartyoutubetv2.common.app.models.playback.managers;
import android.app.Activity;
import androidx.annotation.NonNull;
import com.liskovsoft.sharedutils.mylogger.Log;
import com.liskovsoft.smartyoutubetv2.common.R;
@ -18,7 +17,6 @@ import java.util.List;
public class AutoFrameRateManager extends PlayerEventListenerHelper {
private static final String TAG = AutoFrameRateManager.class.getSimpleName();
private final HqDialogManager mUiManager;
private boolean mMainActivityRunOnce;
private FormatItem mSelectedVideoTrack;
private final AutoFrameRateHelper mAutoFrameRateHelper;
private final ModeSyncManager mModeSyncManager;
@ -67,17 +65,10 @@ public class AutoFrameRateManager extends PlayerEventListenerHelper {
}
@Override
public void onActivity(Activity activity) {
super.onActivity(activity);
if (!mMainActivityRunOnce) {
restoreAfrData();
addUiOptions();
mAutoFrameRateHelper.saveOriginalState(activity);
mMainActivityRunOnce = true;
}
public void onInitDone() {
restoreAfrData();
addUiOptions();
mAutoFrameRateHelper.saveOriginalState(mActivity);
}
@Override

View File

@ -1,10 +1,8 @@
package com.liskovsoft.smartyoutubetv2.common.app.models.playback.managers;
import android.app.Activity;
import android.os.Build.VERSION;
import com.liskovsoft.smartyoutubetv2.common.R;
import com.liskovsoft.smartyoutubetv2.common.app.models.playback.PlayerEventListenerHelper;
import com.liskovsoft.smartyoutubetv2.common.app.models.playback.controller.PlaybackController;
import com.liskovsoft.smartyoutubetv2.common.app.models.playback.controller.PlaybackEngineController;
import com.liskovsoft.smartyoutubetv2.common.app.models.playback.ui.OptionItem;
import com.liskovsoft.smartyoutubetv2.common.app.models.playback.ui.UiOptionItem;
@ -30,17 +28,9 @@ public class HqDialogManager extends PlayerEventListenerHelper {
private final List<Runnable> mHideListeners = new ArrayList<>();
@Override
public void onActivity(Activity activity) {
super.onActivity(activity);
public void onInitDone() {
mSettingsPresenter = AppSettingsPresenter.instance(mActivity);
}
@Override
public void onController(PlaybackController controller) {
super.onController(controller);
controller.setBuffer(AppPrefs.instance(mActivity).getVideoBufferType(PlaybackEngineController.BUFFER_LOW));
mController.setBuffer(AppPrefs.instance(mActivity).getVideoBufferType(PlaybackEngineController.BUFFER_LOW));
}
private void addQualityCategories() {

View File

@ -53,10 +53,8 @@ public class PlayerUiManager extends PlayerEventListenerHelper implements Metada
}
@Override
public void onActivity(Activity activity) {
super.onActivity(activity);
AppSettingsPresenter.instance(activity).setPlayerUiManager(this);
public void onInitDone() {
AppSettingsPresenter.instance(mActivity).setPlayerUiManager(this);
}
@Override

View File

@ -1,6 +1,6 @@
package com.liskovsoft.smartyoutubetv2.common.app.models.playback.managers;
import android.app.Activity;
import androidx.annotation.NonNull;
import com.liskovsoft.sharedutils.helpers.Helpers;
import com.liskovsoft.smartyoutubetv2.common.R;
import com.liskovsoft.smartyoutubetv2.common.app.models.data.Video;
@ -17,43 +17,26 @@ import java.util.List;
import java.util.Map;
public class StateUpdater extends PlayerEventListenerHelper {
private static final long MUSIC_VIDEO_LENGTH_MS = 6 * 60 * 1000;
private static final int MAX_STATE_SIZE = 10;
private boolean mIsPlaying;
private FormatItem mVideoFormat;
private FormatItem mAudioFormat;
private FormatItem mSubtitleFormat;
private static final long MUSIC_VIDEO_LENGTH_MS = 6 * 60 * 1000;
// Don't store state inside Video object.
// As one video might correspond to multiple Video objects.
private final Map<String, State> mStates = new HashMap<>();
private float mLastSpeed = -1;
private static class State {
public final long positionMs;
private final long lengthMs;
public final float speed;
public State(long positionMs) {
this(positionMs, -1);
}
public State(long positionMs, long lengthMs) {
this(positionMs, lengthMs, 1.0f);
}
public State(long positionMs, long lengthMs, float speed) {
this.positionMs = positionMs;
this.lengthMs = lengthMs;
this.speed = speed;
}
}
@Override
public void onActivity(Activity activity) {
super.onActivity(activity);
public void onInitDone() {
AppPrefs prefs = AppPrefs.instance(mActivity);
mVideoFormat = prefs.getFormat(FormatItem.TYPE_VIDEO, FormatItem.VIDEO_HD_AVC);
mAudioFormat = prefs.getFormat(FormatItem.TYPE_AUDIO, FormatItem.AUDIO_HQ_MP4A);
mSubtitleFormat = prefs.getFormat(FormatItem.TYPE_SUBTITLE, null);
mVideoFormat = AppPrefs.instance(mActivity).getFormat(FormatItem.TYPE_VIDEO, FormatItem.VIDEO_HD_AVC);
mAudioFormat = AppPrefs.instance(mActivity).getFormat(FormatItem.TYPE_AUDIO, FormatItem.AUDIO_HQ_MP4A);
mSubtitleFormat = AppPrefs.instance(mActivity).getFormat(FormatItem.TYPE_SUBTITLE, null);
String data = prefs.getStateUpdaterData();
restoreState(data);
}
/**
@ -103,6 +86,7 @@ public class StateUpdater extends PlayerEventListenerHelper {
@Override
public void onEngineReleased() {
saveState();
persistState();
}
@Override
@ -173,12 +157,50 @@ public class StateUpdater extends PlayerEventListenerHelper {
Video video = mController.getVideo();
if (video != null) {
mStates.put(video.videoId, new State(mController.getPositionMs(), mController.getLengthMs(), mController.getSpeed()));
mStates.put(video.videoId, new State(video.videoId, mController.getPositionMs(), mController.getLengthMs(), mController.getSpeed()));
}
mLastSpeed = mController.getSpeed();
}
private void persistState() {
AppPrefs prefs = AppPrefs.instance(mActivity);
StringBuilder sb = new StringBuilder();
int counter = 0;
for (State state : mStates.values()) {
counter++;
if (sb.length() != 0) {
sb.append("|");
}
sb.append(state);
if (counter >= MAX_STATE_SIZE) {
break;
}
}
prefs.setStateUpdaterData(sb.toString());
}
private void restoreState(String data) {
if (data == null) {
return;
}
String[] split = data.split("\\|");
for (String spec : split) {
State state = State.from(spec);
if (state != null) {
mStates.put(state.videoId, state);
}
}
}
private void trimStorage() {
// NOP
}
@ -208,7 +230,7 @@ public class StateUpdater extends PlayerEventListenerHelper {
// internal storage has priority over item data loaded from network
if (state == null && item.percentWatched > 0 && item.percentWatched < 100) {
state = new State(getNewPosition(item.percentWatched));
state = new State(item.videoId, getNewPosition(item.percentWatched));
}
if (state != null) {
@ -269,10 +291,55 @@ public class StateUpdater extends PlayerEventListenerHelper {
for (float speed : speedValues) {
items.add(UiOptionItem.from(
String.valueOf(speed),
optionItem -> {
mController.setSpeed(speed);
},
optionItem -> mController.setSpeed(speed),
mController.getSpeed() == speed));
}
}
private static class State {
public final String videoId;
public final long positionMs;
private final long lengthMs;
public final float speed;
public State(String videoId, long positionMs) {
this(videoId, positionMs, -1);
}
public State(String videoId, long positionMs, long lengthMs) {
this(videoId, positionMs, lengthMs, 1.0f);
}
public State(String videoId, long positionMs, long lengthMs, float speed) {
this.videoId = videoId;
this.positionMs = positionMs;
this.lengthMs = lengthMs;
this.speed = speed;
}
public static State from(String spec) {
if (spec == null) {
return null;
}
String[] split = spec.split(",");
if (split.length != 4) {
return null;
}
String videoId = split[0];
long positionMs = Helpers.parseInt(split[1]);
long lengthMs = Helpers.parseInt(split[2]);
float speed = Helpers.parseFloat(split[3]);
return new State(videoId, positionMs, lengthMs, speed);
}
@NonNull
@Override
public String toString() {
return String.format("%s,%s,%s,%s", videoId, positionMs, lengthMs, speed);
}
}
}

View File

@ -36,10 +36,8 @@ public class VideoLoader extends PlayerEventListenerHelper implements MetadataLi
}
@Override
public void onActivity(Activity activity) {
super.onActivity(activity);
mRepeatMode = AppPrefs.instance(activity).getVideoLoaderData(PlaybackUiController.REPEAT_ALL);
public void onInitDone() {
mRepeatMode = AppPrefs.instance(mActivity).getVideoLoaderData(PlaybackUiController.REPEAT_ALL);
}
@Override

View File

@ -16,6 +16,7 @@ public class AppPrefs extends SharedPreferencesBase {
private static final String AUTO_FRAME_RATE_DATA = "auto_frame_rate_data";
private static final String BACKUP_DATA = "backup_data";
private static final String VIDEO_LOADER_DATA = "video_loader_data";
private static final String STATE_UPDATER_DATA = "state_updater_data";
private String mDefaultDisplayMode;
private String mCurrentDisplayMode;
@ -95,4 +96,12 @@ public class AppPrefs extends SharedPreferencesBase {
public void setVideoLoaderData(int data) {
putInt(VIDEO_LOADER_DATA, data);
}
public String getStateUpdaterData() {
return getString(STATE_UPDATER_DATA, null);
}
public void setStateUpdaterData(String data) {
putString(STATE_UPDATER_DATA, data);
}
}

BIN
images/screen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@ -1,9 +1,10 @@
package com.liskovsoft.smartyoutubetv2.tv.ui.widgets.textbadgecard;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;
import androidx.leanback.widget.ImageCardView;
@ -11,6 +12,8 @@ import com.liskovsoft.smartyoutubetv2.tv.R;
public class TextBadgeImageCardView extends ImageCardView {
private TextBadgeImageView mTextBadgeImageLayout;
private Handler mHandler;
private Runnable mEnableMarquee;
public TextBadgeImageCardView(Context context) {
super(context);
@ -30,6 +33,11 @@ public class TextBadgeImageCardView extends ImageCardView {
init();
}
private void init() {
mTextBadgeImageLayout = findViewById(R.id.main_image_wrapper);
mHandler = new Handler(Looper.getMainLooper());
}
private void enableTextAnimation(boolean enable) {
TextView title = findViewById(R.id.title_text);
TextView content = findViewById(R.id.content_text);
@ -39,21 +47,39 @@ public class TextBadgeImageCardView extends ImageCardView {
}
if (enable) {
title.setEllipsize(TruncateAt.MARQUEE);
title.setMarqueeRepeatLimit(-1);
title.setHorizontallyScrolling(true);
mEnableMarquee = () -> enableMarquee(title, content);
content.setEllipsize(TruncateAt.MARQUEE);
content.setMarqueeRepeatLimit(-1);
content.setHorizontallyScrolling(true);
mHandler.postDelayed(mEnableMarquee, 1_000);
} else {
title.setEllipsize(TruncateAt.END);
content.setEllipsize(TruncateAt.END);
if (mEnableMarquee != null) {
mHandler.removeCallbacks(mEnableMarquee);
mEnableMarquee = null;
}
disableMarquee(title, content);
}
}
private void init() {
mTextBadgeImageLayout = findViewById(R.id.main_image_wrapper);
private void disableMarquee(TextView... textViews) {
if (textViews == null || textViews.length == 0) {
return;
}
for (TextView textView : textViews) {
textView.setEllipsize(TruncateAt.END);
}
}
private void enableMarquee(TextView... textViews) {
if (textViews == null || textViews.length == 0) {
return;
}
for (TextView textView : textViews) {
textView.setEllipsize(TruncateAt.MARQUEE);
textView.setMarqueeRepeatLimit(-1);
textView.setHorizontallyScrolling(true);
}
}
/**