feat: 升级到v3.0.9版本

- 新增直播开关控制功能,可隐藏/显示直播tab
- 优化滑杆圆球大小至20dp直径,提升操作体验
- 改进滑杆刻度显示,非激活轨道显示刻度
- 增强播放进度条动态大小调整功能
- 新增实时倍速显示功能
- 优化源管理模块间距动态调整
- 修复播放进度条圆球跳回问题
- 完善直播开关逻辑和UI交互
- 更新版本号至3.0.9
This commit is contained in:
您的名字
2025-10-24 14:00:14 +08:00
parent dcc751c691
commit 78570eed7c
38 changed files with 487 additions and 159 deletions
@@ -324,4 +324,12 @@ public class Setting {
public static void setPrivacyAgreed(boolean agreed) {
Prefers.put("privacy_agreed_v1", agreed);
}
public static boolean isLiveTabVisible() {
return Prefers.getBoolean("live_tab_visible", true);
}
public static void putLiveTabVisible(boolean visible) {
Prefers.put("live_tab_visible", visible);
}
}
@@ -17,7 +17,7 @@ public abstract class ConfigDao extends BaseDao<Config> {
@Query("SELECT * FROM Config WHERE type = :type ORDER BY time DESC")
public abstract List<Config> findByType(int type);
@SuppressWarnings(RoomWarnings.QUERY_MISMATCH)
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT id, name, url, type, time FROM Config WHERE type = :type ORDER BY time DESC")
public abstract List<Config> findUrlByType(int type);
@@ -2,7 +2,9 @@ package com.fongmi.android.tv.ui.custom;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -33,6 +35,7 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
private long currentPosition;
private long currentBuffered;
private boolean scrubbing;
private boolean isPressed;
public CustomSeekView(Context context) {
this(context, null);
@@ -55,6 +58,28 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
timeBar = findViewById(R.id.timeBar);
timeBar.addListener(this);
refresh = this::refresh;
// 设置触摸事件监听器,实现动态尺寸调整
timeBar.setOnTouchListener((v, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!isPressed) {
isPressed = true;
// 按下时:滑杆4dp,圆球16dp
setTimeBarSize(4, 16);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (isPressed) {
isPressed = false;
// 松开时:滑杆2dp,圆球12dp
setTimeBarSize(2, 12);
}
break;
}
return false; // 不拦截事件,让DefaultTimeBar正常处理
});
}
public void setListener(Players player) {
@@ -68,6 +93,58 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
public void setDuration(long duration) {
timeBar.setDuration(duration);
}
/**
* 动态设置进度条高度和拖拽手柄大小
* @param barHeightDp 滑杆高度值(dp
* @param scrubberSizeDp 拖拽手柄大小(dp
*/
private void setTimeBarSize(int barHeightDp, int scrubberSizeDp) {
// 设置滑杆高度
int barHeightPx = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
barHeightDp,
getContext().getResources().getDisplayMetrics()
);
// 设置拖拽手柄大小
int scrubberSizePx = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
scrubberSizeDp,
getContext().getResources().getDisplayMetrics()
);
// 通过反射设置DefaultTimeBar的内部属性
try {
// 设置滑杆高度
java.lang.reflect.Field barHeightField = timeBar.getClass().getDeclaredField("barHeight");
barHeightField.setAccessible(true);
barHeightField.setInt(timeBar, barHeightPx);
// 设置拖拽手柄大小 - 尝试多个可能的字段名
String[] scrubberFields = {"scrubberSize", "scrubberEnabledSize", "scrubberDisabledSize"};
for (String fieldName : scrubberFields) {
try {
java.lang.reflect.Field scrubberField = timeBar.getClass().getDeclaredField(fieldName);
scrubberField.setAccessible(true);
scrubberField.setInt(timeBar, scrubberSizePx);
break; // 成功设置后退出循环
} catch (NoSuchFieldException e) {
// 继续尝试下一个字段名
}
}
// 刷新视图
timeBar.requestLayout();
timeBar.invalidate();
} catch (Exception e) {
// 如果反射失败,使用备用方案
e.printStackTrace();
// 备用方案:重新设置布局参数
timeBar.getLayoutParams().height = barHeightPx;
timeBar.requestLayout();
}
}
private void start() {
removeCallbacks(refresh);
@@ -137,12 +214,15 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
// 延迟刷新进度条,确保播放器已经处理了跳转操作
removeCallbacks(refresh);
postDelayed(() -> {
refresh();
// 确保进度条位置与实际播放位置一致
long actualPosition = player.getPosition();
if (Math.abs(actualPosition - positionMs) > 100) { // 如果差异超过100ms,再次调整
timeBar.setPosition(actualPosition);
positionView.setText(player.stringToTime(actualPosition));
// 只有在非拖动状态下才刷新进度条位置
if (!scrubbing) {
refresh();
// 确保进度条位置与实际播放位置一致
long actualPosition = player.getPosition();
if (Math.abs(actualPosition - positionMs) > 100) { // 如果差异超过100ms,再次调整
timeBar.setPosition(actualPosition);
positionView.setText(player.stringToTime(actualPosition));
}
}
}, 50); // 延迟50ms刷新
}
@@ -168,9 +248,10 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
public void onScrubStop(@NonNull TimeBar timeBar, long position, boolean canceled) {
scrubbing = false;
if (!canceled) {
// 先隐藏进度条提示,避免用户看到不一致的状态
// 注意:这里不能直接访问mBinding.widget.seek,因为CustomSeekView不包含这个引用
// 这个优化将在VideoActivity的onSeekEnd方法中实现
// 立即设置进度条位置到目标位置,避免圆球跳回原始位置
timeBar.setPosition(position);
positionView.setText(player.stringToTime(position));
// 调整播放位置
seekToTimeBarPosition(position);
// 确保播放状态正确
@@ -73,6 +73,7 @@
<string name="setting_subscription">订阅管理</string>
<string name="setting_player">播放设置</string>
<string name="setting_incognito">无痕模式</string>
<string name="setting_live_tab_visible">隐藏直播</string>
<string name="setting_quality">图片品质</string>
<string name="setting_size">图片尺寸</string>
<string name="setting_doh">DoH</string>
+1
View File
@@ -78,6 +78,7 @@
<string name="setting_data">Data Management</string>
<string name="setting_player">Player setting</string>
<string name="setting_incognito">Incognito mode</string>
<string name="setting_live_tab_visible">Hide Live Tab</string>
<string name="setting_quality">Image quality</string>
<string name="setting_size">Image size</string>
<string name="setting_doh">DoH</string>