diff --git a/README.md b/README.md index b02d4fce..6041ec18 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,10 @@ [![vue version](https://img.shields.io/badge/vue-3.2-green.svg)](https://github.com/vuejs/core) [![vue version](https://img.shields.io/badge/@quicktvui/quicktvui3-latest-green.svg)](https://www.npmjs.com/package/@quicktvui/quicktvui3?activeTab=versions) -基于 [QuickTVUI](http://v3.quicktvui.com/zh/) 专为快速打造安卓TV影视应用而开发的示例项目。 -项目主要目的是让开发者通过对本项目的简单修改,实现一些TV端常见的功能页面,从而加速开发者开发TV类影视类应用。 -主要页面包括瀑布流首页、媒资列表、搜索、视频观看详情等,以下是一些页面效果: +HelloTV是基于 [QuickTVUI](http://v3.quicktvui.com/zh/) 框架的视频点播直播应用,面向使用遥控器交互的安卓大屏设备。 +项目目的是让开发者通过对本项目源码的简单修改,快速实现一些TV端常见的功能页面,从而加速开发进度。如果使用者并不具备编程能力,可以参考“HelloTV-Case”项目,简单修改该项目配置文件就可以快速实现一个标准化的视频类应用。 +点播页面包括瀑布流首页、内容列表、筛选、搜索、详情、历史、收藏、登录等,直播页面包括多级列表页、播放详情页等。 +以下是一些页面效果: 瀑布流首页 ![image](https://extcdn.hsrc.tv/extend_screen/images/example_app/bgplay.png) @@ -15,17 +16,11 @@ ![image](https://extcdn.hsrc.tv/extend_screen/images/example_app/filter.png) 详情页 ![image](https://extcdn.hsrc.tv/extend_screen/images/example_app/detail.png) -多级列表页 -![image](doc/live1.png) -![image](doc/live2.png) 内容编辑页 ![image](doc/edit.png) -> 目前项目仅供参考,代码逐步完善中,请暂勿用于正式项目中。 -> release版本会于近期推出,敬请期待! - ## 快速开始 @@ -87,11 +82,11 @@ npm run build-apk-release - [x] 瀑布流首页 - [x] 搜索页 - [x] 媒资详情页 -- [x] 筛选页(开发中) +- [x] 筛选页 - [x] 多级列表页 - [x] 一键打包生成APK -以上页面开发完成,正在debug中 +以上页面开发完成,如发现问题,请提交issue 开发中 - [ ] 内容编辑页 diff --git a/android/app/build.gradle b/android/app/build.gradle index 1138dce2..9ca154fe 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -3,15 +3,37 @@ plugins { } ext { + // ----- APK相关配置 ----- // APP_PACKAGE = 'com.quicktvui.hellotv' // APK包名 - APP_NAME = 'HelloTV' // APK名字(Launcher显示) - APP_VERSION_CODE = 1 // APK版本code + APP_NAME = 'HelloTV' // 桌面显示名称 + APP_VERSION_CODE = 1 // APK版本code(整数,最小为1,覆盖安装需要 >= 上次的code) APP_VERSION_NAME = '1.0.0' // APK版本名称 - BUILD_RPK_IN_APK = true // 是否将rpk打入apk(非assets加载方式不用打包进apk) - RPK_PACKAGE = "es.hellotv" // 生成的rpk包名 - RPK_FILE_NAME = "hello.rpk" // 生成的rpk文件名 + INCLUDE_SO = true // 将so打包进apk(首次启动不需要下载so文件,体积会变大,但首次启动会变快) + SO_ABI = ['armeabi-v7a', 'arm64-v8a'] // 包含SO的架构 + // ----- RPK加载方式 ----- // + LOAD_TYPE = 1 // 加载方式: + // 1 从APK的assets中加载(只从apk中加载rpk,不会更新) + // 2 从指定路径加载 + // 3 从url加载(适合url不变,只更新rpk包) + // 4 从指定源服务器加载 + + RPK_PACKAGE = "es.hellotv" // rpk包名(会按照这个包名进行本地版本管理) + + // LOAD_MODULE修改后,只更改对应加载方式的配置即可 + + // 方式1: + RPK_FILE_NAME = "hello.rpk" // 自动生成的rpk文件名 + + // 方式2: + RPK_FILE_PATH = "data/data/$APP_PACKAGE/files/hello.rpk" + + // 方式3: + RPK_FILE_URL = "https://extcdn.hsrc.tv/data_center/files/plugin/2024/04/18/efe23d2e-0c05-4dac-a77a-11635d6bbf12.zip" + + // 方式4 + REPOSITORY_HOST = "http://repo.quicktvui.com/" vueDistDir = new File(project.rootDir, '../dist/android') assetsDir = new File(project.buildDir, 'assets') @@ -32,13 +54,10 @@ android { multiDexEnabled true - buildConfigField 'String', 'RPK_PACKAGE', "\"${RPK_PACKAGE}\"" - buildConfigField 'String', 'RPK_FILE_NAME', "\"assets://${RPK_FILE_NAME}\"" - - buildConfigField 'boolean', 'IS_BUILD_RPK_IN_APK', "$BUILD_RPK_IN_APK" + buildConfigField 'boolean', 'IS_INCLUDE_SO', "$INCLUDE_SO" ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a' + abiFilters SO_ABI.toArray() as String[] } } @@ -61,6 +80,7 @@ android { signingConfig.initWith(buildTypes.debug.signingConfig) } } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 @@ -68,13 +88,25 @@ android { android.sourceSets.findByName('main').assets.srcDirs += assetsDir + packagingOptions { + pickFirst 'lib/**/libc++_shared.so' + } } dependencies { implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'com.extscreen.runtime:official:2.8.0.1' + implementation 'com.extscreen.runtime:official:2.8.0.5' + implementation 'eskit.sdk.support:video-cache-support:0.0.3-SNAPSHOT' + + if (INCLUDE_SO) { + implementation 'eskit.sdk.support.so:hp-v1-armeabi-v7a:1.0.1' + implementation 'eskit.sdk.support.so:hp-v1-arm64-v8a:1.0.1' + + implementation 'eskit.sdk.support.so:ijk-player-armeabi-v7a:1.0.0' + implementation 'eskit.sdk.support.so:ijk-player-arm64-v8a:1.0.0' + } } task deleteRpkCacheTask(type: Delete) { @@ -105,21 +137,62 @@ task buildVue(type: Exec) { dependsOn(buildVendor) } +task writeConfigFile() { + doFirst { + assetsDir.mkdirs() + def sourceFile = new File(project.projectDir, "config.json") + def configMap = new groovy.json.JsonSlurper().parse(sourceFile, 'UTF-8') + + configMap.put('version', 1) + configMap.put('load_type', LOAD_TYPE) + configMap.put('rpk_package', RPK_PACKAGE) + + def uri = '' + def repo = '' + switch (LOAD_TYPE) { + case 1: + uri = "assets://${RPK_FILE_NAME}" + break + case 2: + uri = "file://${RPK_FILE_PATH}" + configMap.put('rpk_load_uri', ) + break + case 3: + uri = RPK_FILE_URL + break + case 4: + repo = REPOSITORY_HOST + break + default: + throw new RuntimeException("unsupport LOAD_TYPE ${LOAD_TYPE}") + break + } + + configMap.put('rpk_load_uri', uri) + configMap.put('repo', repo) + + def json = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(configMap)) + new groovy.io.GroovyPrintStream(new FileOutputStream(new File(assetsDir, "config.json")), true, 'UTF-8').print(json) + } +} + task zipRpkTask(type: Zip) { archiveName = RPK_FILE_NAME destinationDir = assetsDir from vueDistDir - dependsOn(buildVue) + dependsOn(buildVue, writeConfigFile) } afterEvaluate { tasks.findByName('preBuild').dependsOn(deleteRpkCacheTask) - if(BUILD_RPK_IN_APK){ - ['generateDebugAssets', 'generateReleaseAssets'].each { + ['generateDebugAssets', 'generateReleaseAssets'].each { + if (LOAD_TYPE == 1) { tasks.findByName(it).dependsOn(zipRpkTask) + } else { + tasks.findByName(it).dependsOn(writeConfigFile) } } diff --git a/android/app/config.json b/android/app/config.json new file mode 100644 index 00000000..39f9b52f --- /dev/null +++ b/android/app/config.json @@ -0,0 +1,11 @@ +{ + "config": { + "local": { + "name": "", + "icon": "", + "zhibo": "", + "dianbo": "" + }, + "remote": "" + } +} \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 481bb434..4bead2dd 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -1,21 +1,6 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +-keep class com.quicktvui.hellotv.App { + public ; + public ; +} \ No newline at end of file diff --git a/android/app/src/main/java/com/quicktvui/hellotv/App.java b/android/app/src/main/java/com/quicktvui/hellotv/App.java index 183e8838..3963b7a6 100644 --- a/android/app/src/main/java/com/quicktvui/hellotv/App.java +++ b/android/app/src/main/java/com/quicktvui/hellotv/App.java @@ -3,8 +3,19 @@ import androidx.multidex.MultiDexApplication; import com.extscreen.runtime.EsKitInitHelper; +import com.google.gson.Gson; +import com.quicktvui.hellotv.config.Config; +import com.quicktvui.hellotv.config.ConfigModule; +import com.sunrain.toolkit.utils.FileUtils; +import com.sunrain.toolkit.utils.ToastUtils; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import eskit.sdk.core.InitConfig; +import eskit.sdk.core.internal.EsComponentManager; /** *
@@ -13,17 +24,46 @@ */ public class App extends MultiDexApplication { + public static ExecutorService sExecutor = Executors.newSingleThreadExecutor(); + public static Config sConfig; + @Override public void onCreate() { super.onCreate(); - initESKitSDK(); + sExecutor.execute(WORK_INIT_CONFIG); + sExecutor.execute(WORK_INIT_SDK); } - private void initESKitSDK() { - EsKitInitHelper.init(this, InitConfig.getDefault() - .addFlags(InitConfig.FLAG_DYNAMIC_SO) - ); - } + private final Runnable WORK_INIT_CONFIG = new Runnable() { + @Override + public void run() { + try { + InputStream is = getAssets().open("config.json"); + ByteArrayOutputStream ops = new ByteArrayOutputStream(); + FileUtils.copy(is, ops); + String json = ops.toString(); + sConfig = new Gson().fromJson(json, Config.class); + } catch (Throwable e) { + e.printStackTrace(); + ToastUtils.showLong("获取配置文件失败 " + e.getMessage()); + } + } + }; + + private final Runnable WORK_INIT_SDK = new Runnable() { + @Override + public void run() { + if(sConfig == null) return; + + InitConfig initConfig = InitConfig.getDefault(); + if (!BuildConfig.IS_INCLUDE_SO) { + initConfig.addFlags(InitConfig.FLAG_DYNAMIC_SO); + } + + initConfig.setSdkInitCallback(() -> EsComponentManager.get().registerModule(ConfigModule.class)); + EsKitInitHelper.init(App.this, initConfig); + } + }; } diff --git a/android/app/src/main/java/com/quicktvui/hellotv/DataCreateHelper.java b/android/app/src/main/java/com/quicktvui/hellotv/DataCreateHelper.java index 923bdb95..65169f96 100644 --- a/android/app/src/main/java/com/quicktvui/hellotv/DataCreateHelper.java +++ b/android/app/src/main/java/com/quicktvui/hellotv/DataCreateHelper.java @@ -1,6 +1,9 @@ package com.quicktvui.hellotv; -import java.io.File; +import android.text.TextUtils; + +import com.quicktvui.hellotv.config.Config; +import com.sunrain.toolkit.utils.ToastUtils; import eskit.sdk.core.EsData; @@ -11,49 +14,38 @@ */ public class DataCreateHelper { - /** - * 从Asset加载代码 - **/ - public static EsData createFromAssets() { - if (!BuildConfig.IS_BUILD_RPK_IN_APK) { - throw new RuntimeException("没有开启Assets加载"); - } - EsData data = new EsData(); - data.setAppPackage(BuildConfig.RPK_PACKAGE); - data.setAppLoadUri(BuildConfig.RPK_FILE_NAME); - return data; - } + public static EsData createWithConfig() { - /** - * 从File加载代码 - **/ - public static EsData createFromFile(File file) { - if (!file.exists()) throw new RuntimeException("文件不存在"); - // 如果文件是外置存储卡,请申请文件权限 - EsData data = new EsData(); - data.setAppPackage(BuildConfig.RPK_PACKAGE); - data.setAppLoadUri("file://" + file.getAbsolutePath()); - return data; - } + Config cfg = App.sConfig; + if (TextUtils.isEmpty(cfg.rpkPackage)) { + ToastUtils.showLong("需要设置 rpk_package"); + return null; + } - /** - * 从网络地址加载代码 - **/ - public static EsData createFromUrl(String url) { EsData data = new EsData(); - data.setAppPackage(BuildConfig.RPK_PACKAGE); - data.setAppLoadUri(url); - return data; - } + data.setAppPackage(cfg.rpkPackage); + + switch (cfg.loadType) { + case 4: + if (TextUtils.isEmpty(cfg.repo)) { + ToastUtils.showLong("需要设置 repo"); + return null; + } + data.setRepository(cfg.repo); + break; + case 1: + case 2: + case 3: + if (TextUtils.isEmpty(cfg.rpkLoadUri)) { + ToastUtils.showLong("需要设置 rpk_load_uri"); + return null; + } + data.setAppLoadUri(cfg.rpkLoadUri); + break; + default: + throw new RuntimeException("not support LOAD_TYPE " + cfg.loadType); + } - /** - * 从仓库地址加载代码 - * 需要将代码上传至仓库 - **/ - public static EsData createFromRepoServer(String repo) { - EsData data = new EsData(); - data.setAppPackage(BuildConfig.RPK_PACKAGE); - data.setRepository(repo); return data; } diff --git a/android/app/src/main/java/com/quicktvui/hellotv/MainActivity.java b/android/app/src/main/java/com/quicktvui/hellotv/MainActivity.java index 71687353..f1ffcc4f 100644 --- a/android/app/src/main/java/com/quicktvui/hellotv/MainActivity.java +++ b/android/app/src/main/java/com/quicktvui/hellotv/MainActivity.java @@ -18,18 +18,26 @@ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + App.sExecutor.execute(WORK_START_APP); + } + + private final Runnable WORK_START_APP = () -> { + if (App.sConfig == null) { + finish(); + return; + } // 第一步 设置启动参数 - EsData data = DataCreateHelper.createFromAssets(); - // 另外还支持的加载方式: -// DataCreateHelper.createFromFile(new File("")); -// DataCreateHelper.createFromUrl("https://extcdn.hsrc.tv/data_center/files/plugin/2024/04/12/aa76e1a2-26b3-4a99-a16d-6bd1bd5a3da7.zip"); -// DataCreateHelper.createFromRepoServer("http://nexus.hmon.tv/"); + EsData data = DataCreateHelper.createWithConfig(); + + if (data == null) { + finish(); + return; + } // 第二步 启动 EsManager.get().start(data); - - } + }; @Override protected void onStop() { diff --git a/android/app/src/main/java/com/quicktvui/hellotv/config/Config.java b/android/app/src/main/java/com/quicktvui/hellotv/config/Config.java new file mode 100644 index 00000000..3dd808bb --- /dev/null +++ b/android/app/src/main/java/com/quicktvui/hellotv/config/Config.java @@ -0,0 +1,31 @@ +package com.quicktvui.hellotv.config; + +import androidx.annotation.Keep; + +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; + +/** + *
+ * + *
+ */ +@Keep +public class Config { + + public int version; + + @SerializedName("load_type") + public int loadType; + + @SerializedName("rpk_load_uri") + public String rpkLoadUri; + + @SerializedName("rpk_package") + public String rpkPackage; + + public String repo; + + public JsonObject config; + +} diff --git a/android/app/src/main/java/com/quicktvui/hellotv/config/ConfigModule.java b/android/app/src/main/java/com/quicktvui/hellotv/config/ConfigModule.java new file mode 100644 index 00000000..02ca90d4 --- /dev/null +++ b/android/app/src/main/java/com/quicktvui/hellotv/config/ConfigModule.java @@ -0,0 +1,48 @@ +package com.quicktvui.hellotv.config; + +import android.content.Context; + +import com.quicktvui.hellotv.App; + +import org.json.JSONObject; + +import eskit.sdk.support.EsPromise; +import eskit.sdk.support.PromiseHolder; +import eskit.sdk.support.args.EsMap; +import eskit.sdk.support.module.IEsModule; + +/** + *
+ * + *
+ */ +public class ConfigModule implements IEsModule { + + @Override + public void init(Context context) { + + } + + public void readConfig(EsPromise promise) { + PromiseHolder callback = PromiseHolder.create(promise); + Config config = App.sConfig; + if (config != null) { + try { + EsMap configMap = new EsMap(); + configMap.pushJSONObject(new JSONObject(config.config.toString())); + callback.put("config", configMap); + callback.put("success", true); + } catch (Throwable e) { + e.printStackTrace(); + } + } else { + callback.put("success", false); + } + callback.sendSuccess(); + } + + @Override + public void destroy() { + + } +} diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index cf425e8a..00000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 6ac0a50d..00000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 729e001e..00000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 43c8d103..00000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap/ic_launcher.png similarity index 100% rename from android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap/ic_launcher.png diff --git a/package.json b/package.json index c34d6220..bded35ce 100644 --- a/package.json +++ b/package.json @@ -32,15 +32,15 @@ }, "dependencies": { "@extscreen/es3-ad-player": "1.0.9", - "@extscreen/es3-component": "1.0.17", + "@extscreen/es3-component": "1.0.19", "@extscreen/es3-core": "1.0.13", "@extscreen/es3-player": "1.0.17", - "@extscreen/es3-player-manager": "1.0.21", - "@extscreen/es3-router": "1.0.7", + "@extscreen/es3-player-manager": "1.0.22", + "@extscreen/es3-router": "1.0.11", "@extscreen/es3-video-player": "1.0.23", "@extscreen/es3-vue": "0.0.13", "@extscreen/es3-vue-style-parser": "0.0.8", - "@quicktvui/quicktvui3": "1.1.20", + "@quicktvui/quicktvui3": "1.1.24", "@vue/devtools-api": "^6.5.0", "@vue/runtime-core": "^3.2.21", "@vue/shared": "^3.2.21", diff --git a/src/api/GlobalApiImpl.ts b/src/api/GlobalApiImpl.ts index 0eddab3e..dba403ae 100644 --- a/src/api/GlobalApiImpl.ts +++ b/src/api/GlobalApiImpl.ts @@ -1,4 +1,4 @@ -import ScreenConfig from "../pages/filter/build_data/ScreenConfig" +import FilterConfig from "../pages/filter/build_data/FilterConfig" import {IGlobalApi} from "./IGlobalApi"; import {RequestManager} from "./request/RequestManager"; import {QTTab, QTTabPageData,QTListViewItem,QTTabItem} from "@quicktvui/quicktvui3"; @@ -170,7 +170,7 @@ export function createGlobalApi(): IGlobalApi { const params = requestManager.getParams() const pageParams = { "pageNo": pageNum, - "pageSize": ScreenConfig.screenPageSize, + "pageSize": FilterConfig.screenPageSize, }; const newParams = {...params, ...pageParams}; return requestManager.post(filterContentUrl,{ diff --git a/src/components/bg-player.vue b/src/components/bg-player.vue index 5a111878..8d6138c3 100644 --- a/src/components/bg-player.vue +++ b/src/components/bg-player.vue @@ -4,6 +4,7 @@ :focusable="false" :clipChildren="false" @onChildChanged="onChildChanged" ref="bg_player_replace_child" + markChildSID="bg-player" :replaceOnVisibilityChanged='false' class="bg_player_replace_child" sid="bg_player_replace_child_sid"> @@ -54,6 +55,7 @@ >>>', position) + } mediaSeriesListRef.value?.requestFocus(position) } @@ -174,6 +177,9 @@ export default defineComponent({ } function release(): void { + selectedIndex.value = 0 + initialized = false + isCollapseExpand.value = false mediaSeriesListRef.value?.release() } diff --git a/src/pages/detail/component/media-introduction.vue b/src/pages/detail/component/media-introduction.vue index e3ab47cc..552769c7 100644 --- a/src/pages/detail/component/media-introduction.vue +++ b/src/pages/detail/component/media-introduction.vue @@ -20,6 +20,7 @@ :enableFocusBorder="true" :clipChildren="false" :focusable="true" + :nextFocusName="{top:'headerSearchButton'}" @click="onClick" @focus="onFocus" :style="{'focus-border-color': isMediaTypeFree ? '#FFFFFF' : '#FFD97C'}" diff --git a/src/pages/detail/component/media-list.vue b/src/pages/detail/component/media-list.vue index 7d2d52f6..471cce76 100644 --- a/src/pages/detail/component/media-list.vue +++ b/src/pages/detail/component/media-list.vue @@ -66,9 +66,12 @@ export default defineComponent({ const mediaDataSource = useMediaDataSource() const log = useESLog() const visible = ref(false) + let selectedIndex : number = 0 let itemListId: string + const dataMap = new Map>() + onMounted(() => { eventbus.on('onMediaSeriesLoadData', onMediaSeriesLoadData) }); @@ -92,20 +95,44 @@ export default defineComponent({ } function scrollTo(position: number): void { + if (log.isLoggable(ESLogLevel.DEBUG)) { + log.d(TAG, "-------选集组件----scrollTo------>>>>>" + position) + } mediaSeriesRef.value?.scrollTo(position) } function setSelected(position: number): void { + if (log.isLoggable(ESLogLevel.DEBUG)) { + log.d(TAG, "-------选集组件----setSelected------>>>>>" + position) + } + selectedIndex = position mediaSeriesRef.value?.setSelected(position) } + function getSelectedPosition() : number{ + return selectedIndex + } + + function requestFocus(position:number): void { + mediaSeriesRef.value?.requestFocus(position) + } + function release(): void { + if (log.isLoggable(ESLogLevel.DEBUG)) { + log.d(TAG, "-------选集组件----release------>>>>>") + } + dataMap.clear() mediaSeriesRef.value?.release() } function getMediaList(mediaItemListId: string, pageNo: number) { + if (dataMap.has(pageNo)) { + //TODO 等待左图右文修改获取数据的bug + return + } mediaDataSource.getMediaItemList(mediaItemListId, pageNo, 10) .then((mediaList: Array) => { + dataMap.set(pageNo, mediaList) if (log.isLoggable(ESLogLevel.DEBUG)) { log.d(TAG, "-------getMediaList----success------>>>>>", pageNo, mediaList) } @@ -161,7 +188,9 @@ export default defineComponent({ onGroupItemFocused, scrollTo, setSelected, - release + release, + getSelectedPosition, + requestFocus } }, }); diff --git a/src/pages/detail/component/media-menu-button.vue b/src/pages/detail/component/media-menu-button.vue index 9dbcc8a4..a25acffe 100644 --- a/src/pages/detail/component/media-menu-button.vue +++ b/src/pages/detail/component/media-menu-button.vue @@ -105,7 +105,6 @@ export default defineComponent({ } function requestItemFocus() { - Native.callUIFunction(menuItemRef.value, 'requestFocus', []); Native.callUIFunction(menuItemRef.value, 'requestFocusDirectly'); } diff --git a/src/pages/detail/component/media-menu.vue b/src/pages/detail/component/media-menu.vue index 5c00c7cc..b1fc7732 100644 --- a/src/pages/detail/component/media-menu.vue +++ b/src/pages/detail/component/media-menu.vue @@ -14,7 +14,6 @@ ref="fullScreenButtonRef" :icon="fullButtonNormal" text="全屏" - :autofocus='autofocus' @click="onFullButtonClick" :vip-focus-icon="fullButtonVIPFocused" :focus-icon="fullButtonFocused" /> @@ -52,12 +51,14 @@ import favButtonFocused from "../../../assets/ic_media_fav_button_focused.png" import favButtonVIPFocused from "../../../assets/ic_media_fav_button_vip_focused.png" import favButtonNormal from "../../../assets/ic_media_fav_button_normal.png" -import { useESEventBus } from "@extscreen/es3-core" +import { ESLogLevel, useESEventBus, useESLog } from "@extscreen/es3-core" import { IMediaAuthorization } from "../../../api/media/IMediaAuthorization" import { inject, Ref, ref, watch } from "vue" import { mediaAuthorizationKey } from "../injectionSymbols" import { IMediaMenuButton } from "./IMediaMenuButton" +const TAG = "MEDIA_MENU" + export default defineComponent({ name: "media-menu", components: { @@ -70,7 +71,7 @@ export default defineComponent({ "onMenuFavouriteButtonClick" ], setup(props, context) { - + const log = useESLog() const authenticated = ref(true) const mediaAuthorization: Ref = inject(mediaAuthorizationKey, {} as any) @@ -130,10 +131,17 @@ export default defineComponent({ function requestFullButtonFocus(): void { let array: Array | undefined = fullScreenButtonRef.value if (array) { + if (log.isLoggable(ESLogLevel.DEBUG)) { + log.d(TAG, "-------requestFullButtonFocus-------->>>>>") + } array[0].requestItemFocus() } } + function release(){ + // menuList.value = [] + } + return { init, initMedia, @@ -152,7 +160,8 @@ export default defineComponent({ setAutofocus, autofocus, requestFullButtonFocus, - fullScreenButtonRef + fullScreenButtonRef, + release } } }) diff --git a/src/pages/detail/component/media-player-placeholder.vue b/src/pages/detail/component/media-player-placeholder.vue index f08454dc..1288ad03 100644 --- a/src/pages/detail/component/media-player-placeholder.vue +++ b/src/pages/detail/component/media-player-placeholder.vue @@ -1,15 +1,16 @@ @@ -412,4 +429,38 @@ export default defineComponent({ text-align: right; } +.media-player-float-view-auth-css { + position: absolute; + display: flex; + justify-content: center; + align-items: center; +} + +.media-player-float-view-text-css { + width: 400px; + height: 30px; + font-size: 20px; + color: #F4D297; + text-align: center; +} + +.media-player-float-view-button-css { + width: 200px; + height: 40px; + border-radius: 35px; + background-color: transparent; + display: flex; + justify-content: center; + align-items: center; + margin-top: 40px; +} + +.media-player-float-view-buy-text-css { + width: 200px; + height: 20px; + font-size: 20px; + color: #603314; + text-align: center; +} + diff --git a/src/pages/detail/component/media-player-view.vue b/src/pages/detail/component/media-player-view.vue index 3a48d52e..fa060b61 100644 --- a/src/pages/detail/component/media-player-view.vue +++ b/src/pages/detail/component/media-player-view.vue @@ -91,6 +91,7 @@ >() function onMediaListItemLoad(page: number, mediaList: Array) { - if (mediaCollapseMenuInit) { + if (mediaCollapseMenuInit.value) { nextTick(() => { mediaCollapseMediaListRef.value?.setListData(page, mediaList) }) @@ -230,7 +231,7 @@ export default defineComponent({ } } - let mediaCollapseMenuInit = false + const mediaCollapseMenuInit = ref(false) let media: IMedia @@ -266,17 +267,34 @@ export default defineComponent({ let mediaListItemFocused = false //-------------------------------菜单----------------------------------- + watch( + () => [mediaCollapseRef.value] as const, + ([instance], [oldInstance]) => { + if (instance) { + if (log.isLoggable(ESLogLevel.DEBUG)) { + log.e(TAG, "----watch---initCollapseMenu----->>>>>") + } + collapse = buildCollapseMenu(mediaListVisible.value) + collapseItemIndex = collapse.defaultIndex ?? 0 + collapseItemList = collapse.itemList + mediaCollapseRef.value?.init(collapse) + + initCollapseOrderMenu() + initCollapseSpeedMenu() + initCollapseDefinitionMenu() + initCollapseListMenu() + } + }, + {flush: 'post'} + ) + function initCollapseMenu() { - collapse = buildCollapseMenu(mediaListVisible.value) - collapseItemIndex = collapse.defaultIndex ?? 0 - collapseItemList = collapse.itemList - mediaCollapseRef.value?.init(collapse) - mediaCollapseMenuInit = true + mediaCollapseMenuInit.value = true } //-------------------------------播放顺序----------------------------------- function initCollapseOrderMenu() { - if (mediaCollapseMenuInit) { + if (mediaCollapseMenuInit.value) { nextTick(() => { if (playModeList != null && playModeList != undefined && playModeList.length > 0) { const data = buildPlayModeList(playModeList) @@ -320,7 +338,7 @@ export default defineComponent({ //------------------------------清晰度------------------------------------ function initCollapseDefinitionMenu() { - if (mediaCollapseMenuInit) { + if (mediaCollapseMenuInit.value) { nextTick(() => { if (definitionList) { mediaCollapseDefinitionRef.value?.setListData(buildDefinitionList(definitionList)) @@ -354,7 +372,7 @@ export default defineComponent({ //--------------------------------倍速---------------------------------- function initCollapseSpeedMenu() { - if (mediaCollapseMenuInit) { + if (mediaCollapseMenuInit.value) { nextTick(() => { if (rateList) { mediaCollapseSpeedRef.value?.setListData(buildPlayRateList(rateList)) @@ -385,7 +403,7 @@ export default defineComponent({ //--------------------------------媒资列表---------------------------------- function initCollapseListMenu() { - if (mediaCollapseMenuInit) { + if (mediaCollapseMenuInit.value) { nextTick(() => { if (media) { mediaCollapseMediaListRef.value?.initMedia(media) @@ -502,12 +520,8 @@ export default defineComponent({ case IMediaPlayerViewState.MEDIA_PLAYER_VIEW_STATE_MENU: isMenuShowing.value = true isProgressShowing.value = false - if (!mediaCollapseMenuInit) { + if (!mediaCollapseMenuInit.value) { initCollapseMenu() - initCollapseOrderMenu() - initCollapseSpeedMenu() - initCollapseDefinitionMenu() - initCollapseListMenu() } mediaCollapseRef.value?.expandItem(collapseItemIndex) break @@ -812,7 +826,7 @@ export default defineComponent({ log.d(TAG, "-----------onPlayerRelease------------->>>>") } mediaCollapseMediaListRef.value?.release() - mediaCollapseMenuInit = false + mediaCollapseMenuInit.value = false } function onPlayerReset(): void { @@ -1033,6 +1047,7 @@ export default defineComponent({ // onNextButtonClicked, onNextButtonFocusChanged, + mediaCollapseMenuInit, } }, }); diff --git a/src/pages/detail/component/media-player.vue b/src/pages/detail/component/media-player.vue index caa2d515..976c05f5 100644 --- a/src/pages/detail/component/media-player.vue +++ b/src/pages/detail/component/media-player.vue @@ -1,12 +1,15 @@