getString(R.string.X,p,p)的用法

本文详细介绍了如何在Android应用的string.xml文件中使用%d/%d式来设置textView显示页号,并通过代码示例展示了如何在Java代码中调用getString方法实现动态显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

%d表示的是数字,%s表示的是字符串,当然还有很多别的格式。

在values里面的string.xml里面应该这样写:

   
< string name ="newslistpageshow" >< xliff:g > %d </ xliff:g > / < xliff:g > %d </ xliff:g ></ string >

当然前提是要在<resources>里面加入前缀:

   
< resources xmlns:xliff ="urn:oasis:names:tc:xliff:document:1.2" >

然后在代码里面设置这两个值就可以了:

   
showPage.setText( this .getString(R.string.newslistpageshow, 1 , 2));

当然后面还可以加很多,比方说string.xml文件中有多少个<xliff:g>%d</xliff:g>

代码里面后面就应该给这几个都设上值。

API里面是这样说的:

   
final String getString( int resId, Object... formatArgs) Return a localized formatted string from the application ' s package ' s default string table, substituting the format arguments as defined in Formatter and format(String, Object...).

这种TextView可以用于显示页号。

/********************************************************************* * * Copyright (C), 2010-2022 Oplus. All rights reserved.. * * VENDOR_EDIT * * File : AddFilePanelFragment.kt * * Description : AddFilePanelFragment * * Version : 1.0 * * Date : 2025/02/08 * * Author : W9085798 * * * * ---------------------Revision History: ---------------------------- * * <author> <data> <version> <desc> ***********************************************************************/ package com.oplus.filemanager.addfilepanel.ui import android.annotation.SuppressLint import android.app.Activity import android.os.SystemClock import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.RelativeLayout import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import com.coui.appcompat.button.COUIButton import com.coui.appcompat.button.SingleButtonWrap import com.coui.appcompat.contextutil.COUIContextUtil import com.coui.appcompat.panel.COUIBottomSheetDialog import com.coui.appcompat.panel.COUIBottomSheetDialogFragment import com.coui.appcompat.panel.COUIPanelFragment import com.coui.appcompat.panel.COUIPanelMultiWindowUtils import com.coui.appcompat.tablayout.COUITab import com.coui.appcompat.tablayout.COUITabLayout import com.coui.appcompat.toolbar.COUIToolbar import com.filemanager.common.MyApplication.appContext import com.filemanager.common.base.BaseFileBean import com.filemanager.common.base.BaseFragmentAdapter import com.filemanager.common.constants.Constants import com.filemanager.common.controller.LoadingController import com.filemanager.common.controller.PrivacyPolicyController import com.filemanager.common.helper.MimeTypeHelper import com.filemanager.common.helper.uiconfig.UIConfigMonitor import com.filemanager.common.utils.Injector import com.filemanager.common.utils.KtViewUtils import com.filemanager.common.utils.Log import com.filemanager.common.utils.ModelUtils import com.filemanager.common.utils.SdkUtils import com.filemanager.common.utils.StatusBarUtil import com.filemanager.common.utils.Utils import com.filemanager.common.utils.dp2px import com.filemanager.common.view.ViewPagerWrapperForPC import com.filemanager.common.view.viewpager.RTLViewPager import com.oplus.filemanager.addfilepanel.R import com.oplus.filemanager.addfilepanel.bean.AddFileBean import com.oplus.filemanager.addfilepanel.viewmodel.AddFilePanelViewModel import com.oplus.filemanager.interfaze.categorydoc.IDocumentExtensionType import com.oplus.filemanager.interfaze.main.IMain import kotlin.properties.Delegates class AddFilePanelFragment : COUIPanelFragment(), COUITabLayout.OnTabSelectedListener, PrivacyPolicyController.OnPrivacyPolicyListener { companion object { private const val TAG = "ShortcutFolderAddFilePanelFragment" private const val NUM = 1024 private const val DECIMAL_NUM = 1024f private const val DISMISS_TIME_GAP = 1000L private const val DELAY_TIME = 50L private const val PANEL_MAX_HEIGHT = "panel_max_height" } var lifeCycle: Lifecycle by Delegates.notNull() var title: String? = null var mCurrentPath: String = "" private var disMissTime: Long = 0L private var toolBar: COUIToolbar? = null private var mTabTitle = arrayListOf( appContext.resources.getString(com.filemanager.common.R.string.total), appContext.resources.getString(com.filemanager.common.R.string.string_photos), appContext.resources.getString(com.filemanager.common.R.string.string_videos), appContext.resources.getString(com.filemanager.common.R.string.string_audio), appContext.resources.getString(com.filemanager.common.R.string.string_documents), appContext.resources.getString(com.filemanager.common.R.string.string_other) ) private val mTabTitles = intArrayOf( Constants.TAB_ALL, Constants.TAB_IMAGE, Constants.TAB_VIDEO, Constants.TAB_AUDIO, Constants.TAB_DOCUMENT, Constants.TAB_OTHER ) private var mAddFileDialogInterface: AddFileDialogInterface? = null private var mPages: ArrayList<AddFileListFragment> = ArrayList() private var mViewPager: RTLViewPager? = null private var mTabView: COUITabLayout? = null private var mViewPagerWrapper: ViewPagerWrapperForPC? = null private var addFileBtn: COUIButton? = null private var verticalButtonWrap: SingleButtonWrap? = null private var rootView: ViewGroup? = null private var addFilePanelViewModel: AddFilePanelViewModel? = null private var statisticsSelectTitleView: TextView? = null private var statisticsSelectBodyView: TextView? = null private var contentLayout: RelativeLayout? = null private var buttonDivider: View? = null private var statisticsArrowUpView: ImageView? = null private var statisticsRootView: ViewGroup? = null private var loadingController: LoadingController? = null private var mPosition: Int = 0 var limitCount: Int = 0 private val allFiles: MutableList<BaseFileBean> = mutableListOf() private val imageFiles: MutableList<BaseFileBean> = mutableListOf() private val videoFiles: MutableList<BaseFileBean> = mutableListOf() private val audioFiles: MutableList<BaseFileBean> = mutableListOf() private val docFiles: MutableList<BaseFileBean> = mutableListOf() private val otherFiles: MutableList<BaseFileBean> = mutableListOf() private val mSelectArrayByID: MutableSet<Long> = mutableSetOf() var addFileClickCallback: (selectData: List<AddFileBean>) -> Unit = {} var isExternalReference: Boolean = false private fun setPanelMaxHeight() { if (activity == null) return rootView?.layoutParams?.apply { if (UIConfigMonitor.isCurrentZoomWindowShow() && SdkUtils.isAtLeastR() && !isExternalReference) { val savedHeight: Int = arguments?.getInt(PANEL_MAX_HEIGHT) ?: 0 Log.d(TAG, "setPanelMaxHeight savedHeight = $savedHeight") if (savedHeight == 0) { // 处理浮窗 height = handleZoomWindowHeight(requireActivity(), height) arguments?.putInt(PANEL_MAX_HEIGHT, height) } else { height = savedHeight } Log.d(TAG, "setPanelMaxHeight height = $height") } else { val screenHeight = dp2px(requireActivity(), requireActivity().resources.configuration.screenHeightDp) val panelMaxHeight = COUIPanelMultiWindowUtils.getPanelMaxHeight(requireActivity(), requireActivity().resources.configuration) Log.d(TAG, "setPanelMaxHeight screenHeight:$screenHeight panelH:$panelMaxHeight height:$height") //当是分屏 if (requireActivity().isInMultiWindowMode) { if (StatusBarUtil.checkIsGestureNavMode(requireActivity())) { //全面屏手势导航 height = handleGestureNavModeHeight(requireActivity(), height) } else { // 底部导航栏 height = Math.min(screenHeight, panelMaxHeight) - requireActivity().resources.getDimensionPixelOffset( com.filemanager.common.R.dimen.dimen_40dp ) height = handleNavModeHeight(requireActivity(), height) } } rootView?.layoutParams = this } } } /** * 处理浮窗模式下的高度 */ private fun handleZoomWindowHeight(activity: Activity, height: Int): Int { var result = height val statusBarH = COUIPanelMultiWindowUtils.getStatusBarHeight(activity) val configuration = activity.resources.configuration val paddingBottom = COUIPanelMultiWindowUtils.getPanelMarginBottom(context, configuration) val rect = COUIPanelMultiWindowUtils.getCurrentWindowVisibleRect(activity) if (rect != null) { if (rect.top == 0) { Log.d(TAG, "handleZoomWindowHeight: rect $rect top is invalid, reset") rect.top = activity.resources.getDimensionPixelOffset(com.filemanager.common.R.dimen.panel_min_top_margin) } if (rect.bottom == 0) { val windowH = KtViewUtils.getWindowSize(activity).y rect.bottom = windowH Log.d(TAG, "handleZoomWindowHeight: rect $rect bottom is invalid, reset $windowH") } result = (rect.bottom - rect.top) - paddingBottom - statusBarH Log.d(TAG, "handleZoomWindowHeight: height $result = ${rect.bottom}-${rect.top}-$paddingBottom-$statusBarH") } return result } /** * 处理底部导航栏导航时的高度 */ private fun handleNavModeHeight(it: Activity, height: Int): Int { val navBarHeight = KtViewUtils.getSoftNavigationBarSize(it) var result = height when { ModelUtils.isTablet() -> { //平板竖屏分屏时 if (UIConfigMonitor.instance.isDevicePortrait(it)) { result = height - navBarHeight Log.d(TAG, "handleNavMode pad portrait height:$result") } } UIConfigMonitor.instance.isFoldable(it) -> { // 折叠屏 result = height - navBarHeight Log.d(TAG, "handleNavMode fold height:$result") } else -> { //手机且分屏处于下屏 if (!COUIPanelMultiWindowUtils.isDisplayInUpperWindow(it)) { result = height - navBarHeight Log.d(TAG, "handleNavMode phone lower split screen height:$result") } } } return result } /** * 处理全面屏手势导航时的高度 */ @VisibleForTesting fun handleGestureNavModeHeight(activity: Activity, height: Int): Int { if (COUIPanelMultiWindowUtils.isDisplayInUpperWindow(activity)) { // 上分屏,不用处理 return height } // 处于下分屏,并且显示了手势指示条 if (StatusBarUtil.checkShowGestureNavBar(activity)) { val result = height - activity.resources.getDimensionPixelOffset(com.filemanager.common.R.dimen.dimen_16dp) Log.d(TAG, "handleGestureNavMode lower split screen with show gesture nvaBar height:$result") return result } return height } override fun onDestroy() { Log.d(TAG, "onDestroy") super.onDestroy() verticalButtonWrap?.release() clearDataList() addFilePanelViewModel?.onDestroy() } override fun initView(panelView: View?) { activity?.let { activity -> LayoutInflater.from(activity).inflate(getLayoutResId(), null, false)?.let { (contentView as? ViewGroup)?.addView(it) initContentView(it) initViewModel(activity) startObserver() initData() } } } private fun getLayoutResId(): Int { return R.layout.add_file_panel_dialog } private fun initViewModel(act: FragmentActivity) { addFilePanelViewModel = ViewModelProvider(act)[AddFilePanelViewModel::class.java] } private fun startObserver() { addFilePanelViewModel?.mAllFiles?.observe(this) { it ?: return@observe showLoadingView(false) setViewPagerData(it) } addFilePanelViewModel?.mAllSelectFiles?.observe(this) { it ?: return@observe updateStatisticsSelectView(it) } } fun notifyUpdate() { mSelectArrayByID.clear() addFilePanelViewModel?.mAllSelectFiles?.value?.forEach { bean -> if (bean.isSelected) { bean.mFileID?.let { mSelectArrayByID.add(it) } } } getCurrentFragment()?.apply { setSelectedData(mSelectArrayByID) notifyUpdate() } } private fun initContentView(view: View) { Log.d(TAG, "initContentView") hideDragView() rootView = view.findViewById(R.id.dialog_layout) toolBar = view.findViewById(R.id.toolbar) mTabView = view.findViewById<COUITabLayout?>(R.id.tab_layout) mViewPager = view.findViewById(R.id.viewPager) mViewPagerWrapper = view.findViewById<ViewPagerWrapperForPC?>(R.id.view_pager_wrapper).apply { notifyMainViewPager = object : ((Boolean) -> Unit) { override fun invoke(enable: Boolean) { activity?.let { val mainAction = Injector.injectFactory<IMain>() mainAction?.setViewPagerScrollEnabled(it, enable) } } } } addFileBtn = view.findViewById<COUIButton?>(R.id.btn_add_file).apply { verticalButtonWrap = SingleButtonWrap(this, SingleButtonWrap.Type.Small) setOnClickListener { Log.d(TAG, "addFileBtn click. select:${addFilePanelViewModel?.mAllSelectFiles?.value?.size}") val selectList = addFilePanelViewModel?.mAllSelectFiles?.value selectList?.let { addFileClickCallback.invoke(it) } } } contentLayout = view.findViewById(R.id.content_layout) buttonDivider = view.findViewById(R.id.button_divider) statisticsSelectTitleView = view.findViewById<TextView?>(R.id.select_title_content) statisticsSelectBodyView = view.findViewById<TextView?>(R.id.select_body_content) statisticsArrowUpView = view.findViewById(R.id.select_arraw_up) statisticsRootView = view.findViewById<ViewGroup?>(R.id.select_root_view).apply { setOnClickListener { if (Utils.isQuickClick()) { Log.w(TAG, "click too fast, try later") return@setOnClickListener } addFilePanelViewModel?.mAllSelectFiles?.value?.let { mAddFileDialogInterface?.replaceSelectedPanelFragment(it) } } } setPanelMaxHeight() } private fun initData() { initToolBar() resetStatisticsView() val fragments = childFragmentManager.fragments for (i in mTabTitles.indices) { initFragment(i, fragments) } initViewPager() if (PrivacyPolicyController.hasAgreePrivacy()) { loadData() } else { PrivacyPolicyController.bindPrivacyPolicyListener(this) } } private fun loadData() { Log.d(TAG, "loadData") showLoadingView(true) // 加载数据 activity?.let { addFilePanelViewModel?.loadAllFiles(it) } } private fun resetStatisticsView() { addFileBtn?.isEnabled = false statisticsSelectTitleView?.apply { visibility = View.VISIBLE text = appContext.resources.getString(com.filemanager.common.R.string.not_selected_file) } statisticsSelectBodyView?.apply { visibility = View.GONE text = "" } statisticsArrowUpView?.apply { visibility = View.GONE } statisticsRootView?.apply { isClickable = false } } private fun initToolBar() { toolBar?.apply { visibility = View.VISIBLE title = appContext.resources.getString(com.filemanager.common.R.string.encryption_file_select_title) isTitleCenterStyle = true inflateMenu(R.menu.add_file_panel_dialog_menu_bar) menu.findItem(R.id.cancel).setOnMenuItemClickListener { mAddFileDialogInterface?.dismissAddFileDialog() true } } if ((mTabView != null) && (mViewPager != null)) { mTabView?.let { it.setupWithViewPager(mViewPager, false) it.addOnTabSelectedListener(this) it.isUpdateindicatorposition = true } } } fun setAddFileDialogInterface(addFileDialog: AddFileDialogInterface) { mAddFileDialogInterface = addFileDialog } private fun initViewPager() { mViewPager?.let { it.offscreenPageLimit = mTabTitles.size it.adapter = ViewPagerFragmentStateAdapter(this) it.currentItem = 0 it.overScrollMode = View.OVER_SCROLL_NEVER } } private fun initFragment(position: Int, fragments: List<Fragment>): Fragment { Log.d(TAG, "initFragment") var fragment = if (fragments.isEmpty()) null else fragments[position] if (fragment == null) { fragment = AddFileListFragment() fragment.isExternalReference = isExternalReference fragment.lifeCycle = lifeCycle fragment.mOnItemSelectCallback = { _, fileID -> mSelectArrayByID.add(fileID) addFilePanelViewModel?.updateAllSelectFiles(mSelectArrayByID) } fragment.mOnItemUnSelectCallback = { _, fileID -> mSelectArrayByID.remove(fileID) addFilePanelViewModel?.updateAllSelectFiles(mSelectArrayByID) } fragment.mFileEmptyCallback = { isShowEmpty -> handleFileEmptyView(isShowEmpty) } fragment.mCurrentPath = mCurrentPath fragment.limitCount = limitCount } if (fragment is AddFileListFragment) { mPages.add(fragment) } return fragment } private fun handleFileEmptyView(isShowEmptyView: Boolean) { if (isShowEmptyView) { showLoadingView(false) buttonDivider?.alpha = 0F } else { buttonDivider?.alpha = 1F } } private fun showLoadingView(isShow: Boolean) { if (isShow) { Log.d(TAG, "showLoadingView disMissTime $disMissTime") activity?.let { loadingController = LoadingController(it, this) //如果异常loadingView刚刚调用,就不调用显示loadingView,防止loadingView一直显示 Log.d(TAG, "showLoadingView getLoadingShowStartTime ${loadingController?.getLoadingShowStartTime()}") if (SystemClock.elapsedRealtime() - disMissTime >= DISMISS_TIME_GAP || loadingController?.getLoadingShowStartTime() == 0L) { loadingController?.showLoading(contentLayout) } } } else { Log.d(TAG, "showLoadingView dismissLoading") loadingController?.dismissLoading() disMissTime = SystemClock.elapsedRealtime() } } override fun onShow(isShowOnFirstPanel: Boolean?) { super.onShow(isShowOnFirstPanel) ((parentFragment as? COUIBottomSheetDialogFragment)?.dialog as? COUIBottomSheetDialog) ?.setPanelBackgroundTintColor(COUIContextUtil.getAttrColor(context, com.support.appcompat.R.attr.couiColorBackgroundElevatedWithCard)) } private fun clearDataList() { allFiles.clear() imageFiles.clear() videoFiles.clear() audioFiles.clear() docFiles.clear() otherFiles.clear() mSelectArrayByID.clear() } private fun setViewPagerData(data: MutableList<AddFileBean>) { Log.d(TAG, "setViewPagerData") clearDataList() allFiles.addAll(data) val documentExtensionType = Injector.injectFactory<IDocumentExtensionType>() data.forEach { file -> val localType = file.mLocalType if (MimeTypeHelper.isImageType(localType)) { imageFiles.add(file) } else if (MimeTypeHelper.isVideoType(localType)) { videoFiles.add(file) } else if (MimeTypeHelper.isAudioType(localType)) { audioFiles.add(file) } else if ((MimeTypeHelper.isDocType(localType) || MimeTypeHelper.isOtherDocType(localType)) && documentExtensionType?.isCategoryDocSupportType(file) == true ) { //isCategoryDocSupportType确保面板上文档分类显示的内容和文管文档分类一致(部分未知格式后缀会识解析TXT类型) docFiles.add(file) } else { otherFiles.add(file) } } updateData(mTabTitles[mPosition]) } private fun updateStatisticsSelectView(data: MutableList<AddFileBean>) { if (data.size <= 0) { Log.d(TAG, "unselect file") resetStatisticsView() return } addFileBtn?.isEnabled = true statisticsSelectTitleView?.apply { visibility = View.VISIBLE text = appContext.resources.getQuantityString( com.filemanager.common.R.plurals.mark_selected_items_new, data.size, data.size ) } statisticsSelectBodyView?.apply { visibility = View.VISIBLE val size: Long = data.sumOf { it.mSize } val totalSize: String = updateSizeFormat(size) text = appContext.resources.getString(com.filemanager.common.R.string.selected_size, totalSize) } statisticsArrowUpView?.apply { visibility = View.VISIBLE } statisticsRootView?.apply { isClickable = true } } @SuppressLint("DefaultLocale") fun updateSizeFormat(size: Long): String { return if (size < NUM) { size.toString() + "B" } else if (size < NUM * NUM) { String.format("%.1f", (size / DECIMAL_NUM)) + " KB" } else if (size < NUM * NUM * NUM) { String.format("%.1f", (size / NUM / DECIMAL_NUM)) + " MB" } else { String.format("%.1f", (size / NUM / NUM / DECIMAL_NUM)) + " GB" } } private fun updateData(fragmentType: Int) { val fragment = getCurrentFragment() fragment?.mFragmentType = fragmentType when (fragmentType) { Constants.TAB_ALL -> fragment?.setData(allFiles) Constants.TAB_IMAGE -> fragment?.setData(imageFiles) Constants.TAB_VIDEO -> fragment?.setData(videoFiles) Constants.TAB_AUDIO -> fragment?.setData(audioFiles) Constants.TAB_DOCUMENT -> fragment?.setData(docFiles) Constants.TAB_OTHER -> fragment?.setData(otherFiles) } fragment?.setSelectedData(mSelectArrayByID) Log.d( TAG, "allFiles=${allFiles.size}, imageFiles=${imageFiles.size}, videoFiles=${videoFiles.size}, " + "audioFiles=${audioFiles.size}, docFiles=${docFiles.size}, otherFiles=${otherFiles.size}" ) } override fun onTabSelected(tab: COUITab?) { Log.d(TAG, "onTabSelected tab.postion=${tab?.position}") tab?.let { mPosition = it.position if (loadingController?.isShowing() != true) { updateData(mTabTitles[mPosition]) } } } override fun onTabUnselected(p0: COUITab?) {} override fun onTabReselected(p0: COUITab?) {} private fun getCurrentFragment(): AddFileListFragment? { return if (mPosition < mPages.size) { mPages[mPosition] } else { null } } inner class ViewPagerFragmentStateAdapter(fragment: Fragment) : BaseFragmentAdapter(fragment) { override fun getItemCount(): Int { return mTabTitle.size } override fun createFragment(position: Int): Fragment { return mPages[position] } override fun getPageTitle(position: Int): CharSequence { return mTabTitle[position] } } override fun onAgreeResult(agree: Boolean, noLongerRemind: Boolean) { loadData() } } interface AddFileDialogInterface { fun dismissAddFileDialog() fun replaceSelectedPanelFragment(selectData: MutableList<AddFileBean>) }这是对应的kotlin代码,怎么处理
07-29
package com.videogo.ui.login; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.DatePicker; import android.widget.ImageButton; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.videogo.exception.BaseException; import com.videogo.exception.ErrorCode; import com.videogo.openapi.EZConstants; import com.videogo.openapi.EZPlayer; import androidx.appcompat.app.AppCompatActivity; import com.videogo.openapi.EZOpenSDK; import ezviz.ezopensdk.R; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FanHui extends AppCompatActivity implements SurfaceHolder.Callback { private static final String TAG = "EZPlayback"; private String mAppKey; private String mDeviceSerial; private String mVerifyCode; private String mAccessToken; private int mCameraNo; private TextView mDateTextView; private int mSelectedYear, mSelectedMonth, mSelectedDay; private static final String KEY_APPKEY = "appkey"; private static final String KEY_SERIAL = "serial"; private static final String KEY_VERIFYCODE = "VerifyCode"; private static final String KEY_ACCESSTOKEN = "accessToken"; private static final String KEY_CAMERANO = "cameraNo"; private static final int MSG_PLAYBACK_PLAY_SUCCESS = 3001; private static final int MSG_PLAYBACK_PLAY_FAIL = 3002; // 回放录像相关 private static final String VIDEO_BY_TIME_URL = "https://open.ys7.com/api/lapp/video/by/time"; private static final String PLAYBACK_URL_API = "https://open.ys7.com/api/lapp/v2/live/address/get"; private static final int PROTOCOL_EZOPEN = 1; // EZOpen协议 private ExecutorService mExecutorService; private ListView mListView; private PlaybackAdapter mAdapter; private List<VideoInfo> mVideoList = new ArrayList<>(); // 播放器相关 private EZPlayer mEZPlayer; private SurfaceView mPlaybackSurfaceView; private SurfaceHolder mSurfaceHolder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ez_playback_list_page); // 创建线程池 mExecutorService = Executors.newFixedThreadPool(2); extractParametersFromIntent(); final Calendar calendar = Calendar.getInstance(); mSelectedYear = calendar.get(Calendar.YEAR); mSelectedMonth = calendar.get(Calendar.MONTH); mSelectedDay = calendar.get(Calendar.DAY_OF_MONTH); // 初始化视图 initViews(); // 设置日期显示模块 setupDatePicker(); // 默认加载当天的录像 loadVideosForSelectedDate(); // 初始化SDK initSDK(); } private void initSDK() { try { // 初始化萤石云SDK EZOpenSDK.initLib(getApplication(), mAppKey); EZOpenSDK.getInstance().setAccessToken(mAccessToken); } catch (Exception e) { Log.e(TAG, "SDK初始化失败", e); Toast.makeText(this, "SDK初始化失败", Toast.LENGTH_SHORT).show(); } } private void initViews() { // 查找ListView mListView = findViewById(R.id.listView); if (mListView == null) { Log.e(TAG, "ListView not found with ID listView"); return; } // 初始化适配器 mAdapter = new PlaybackAdapter(); mListView.setAdapter(mAdapter); // 设置点击事件 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { VideoInfo video = mVideoList.get(position); playVideo(video); } }); // 获取播放器视图 - 使用SurfaceView mPlaybackSurfaceView = findViewById(R.id.remote_playback_wnd_sv); if (mPlaybackSurfaceView != null) { // 设置SurfaceHolder回调 mSurfaceHolder = mPlaybackSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); } else { Log.e(TAG, "SurfaceView not found with ID remote_playback_wnd_sv"); } } // SurfaceHolder回调方法 @Override public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "Surface created"); // 当Surface创建时,如果有播放器实例,设置SurfaceHolder if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(holder); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "Surface changed: " + width + "x" + height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "Surface destroyed"); // 当Surface销毁时,释放播放器资源 stopPlayback(); } private void setupDatePicker() { mDateTextView = findViewById(R.id.date_text); ImageButton datePickerButton = findViewById(R.id.date_picker_button); updateDateDisplay(); datePickerButton.setOnClickListener(v -> showDatePickerDialog()); } private void updateDateDisplay() { String formattedDate = String.format(Locale.getDefault(), "%d年%02d月%02d日", mSelectedYear, mSelectedMonth + 1, // 月份需要+1 mSelectedDay); mDateTextView.setText(formattedDate); } private void showDatePickerDialog() { final AlertDialog dlg = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog).create(); dlg.show(); Window window = dlg.getWindow(); window.setContentView(R.layout.datepicker_layout); // 设置对话框宽度 WindowManager.LayoutParams lp = window.getAttributes(); lp.width = WindowManager.LayoutParams.MATCH_PARENT; window.setAttributes(lp); // 获取并初始化 DatePicker DatePicker dpPicker = window.findViewById(R.id.dpPicker); // 隐藏不需要的视图 ViewGroup rootView = (ViewGroup) dpPicker.getChildAt(0); if (rootView != null) { ViewGroup childView = (ViewGroup) rootView.getChildAt(0); if (childView != null) { childView.getChildAt(2).setVisibility(View.VISIBLE); // 确保月选择器可见 childView.getChildAt(1).setVisibility(View.VISIBLE); } } dpPicker.init(mSelectedYear, mSelectedMonth, mSelectedDay, null); // 设置按钮事件 RelativeLayout yesButton = window.findViewById(R.id.YES); RelativeLayout noButton = window.findViewById(R.id.NO); yesButton.setOnClickListener(v -> { mSelectedYear = dpPicker.getYear(); mSelectedMonth = dpPicker.getMonth(); mSelectedDay = dpPicker.getDayOfMonth(); updateDateDisplay(); dlg.dismiss(); // 加载新选择的日期的录像 loadVideosForSelectedDate(); }); noButton.setOnClickListener(v -> dlg.dismiss()); } private void extractParametersFromIntent() { Bundle extras = getIntent().getExtras(); if (extras != null) { mAppKey = extras.getString(KEY_APPKEY, ""); mDeviceSerial = extras.getString(KEY_SERIAL, ""); mVerifyCode = extras.getString(KEY_VERIFYCODE, ""); mAccessToken = extras.getString(KEY_ACCESSTOKEN, ""); mCameraNo = extras.getInt(KEY_CAMERANO, 0); Log.d(TAG, "Received parameters:"); Log.d(TAG, "AppKey: " + mAppKey); Log.d(TAG, "DeviceSerial: " + mDeviceSerial); Log.d(TAG, "VerifyCode: " + mVerifyCode); Log.d(TAG, "AccessToken: " + mAccessToken); Log.d(TAG, "CameraNo: " + mCameraNo); } else { Log.e(TAG, "No parameters received from intent"); } } private void loadVideosForSelectedDate() { // 计算开始和结束时间戳 Calendar cal = Calendar.getInstance(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 0, 0, 0); long startTime = cal.getTimeInMillis(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 23, 59, 59); long endTime = cal.getTimeInMillis(); // 发起网络请求获取录像 fetchVideosByTime(startTime, endTime); } private void fetchVideosByTime(long startTime, long endTime) { mExecutorService.execute(() -> { try { URL url = new URL(VIDEO_BY_TIME_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(10000); conn.setReadTimeout(10000); // 构建POST数据(添加分页参数) StringBuilder postData = new StringBuilder(); postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); postData.append("&deviceSerial=").append(URLEncoder.encode(mDeviceSerial, "UTF-8")); postData.append("&channelNo=").append(mCameraNo); postData.append("&startTime=").append(startTime); postData.append("&endTime=").append(endTime); postData.append("&recType=").append(0); // 系统自动选择 postData.append("&version=2.0"); // 添加分页版本 postData.append("&pageSize=100"); // 添加分页大小 // 发送请求 OutputStream os = conn.getOutputStream(); os.write(postData.toString().getBytes("UTF-8")); os.flush(); os.close(); // 处理响应 int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { // 读取响应内容 InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); // 解析JSON响应 JSONObject json = new JSONObject(response.toString()); String code = json.optString("code", "0"); if ("200".equals(code)) { JSONArray data = json.getJSONArray("data"); List<VideoInfo> videos = parseVideoData(data); runOnUiThread(() -> { mVideoList.clear(); mVideoList.addAll(videos); mAdapter.notifyDataSetChanged(); }); } else { String msg = json.optString("msg", "未知错误"); Log.e(TAG, "获取录像失败: " + msg); runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像失败: " + msg, Toast.LENGTH_SHORT).show()); } } else { Log.e(TAG, "HTTP错误: " + responseCode); runOnUiThread(() -> Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } conn.disconnect(); } catch (Exception e) { Log.e(TAG, "获取录像异常", e); runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } }); } private List<VideoInfo> parseVideoData(JSONArray data) throws JSONException { List<VideoInfo> videos = new ArrayList<>(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); for (int i = 0; i < data.length(); i++) { JSONObject videoObj = data.getJSONObject(i); VideoInfo video = new VideoInfo(); video.id = videoObj.optString("id"); video.startTime = videoObj.optLong("startTime"); video.endTime = videoObj.optLong("endTime"); video.recType = videoObj.optInt("recType"); // 格式化时间显示 Date startDate = new Date(video.startTime); Date endDate = new Date(video.endTime); video.timeRange = sdf.format(startDate) + " - " + sdf.format(endDate); videos.add(video); } return videos; } private void playVideo(VideoInfo video) { // 停止当前播放 stopPlayback(); // 获取播放地址 fetchPlaybackUrl(video); } /** * 获取回放地址并播放 */ private void fetchPlaybackUrl(VideoInfo video) { mExecutorService.execute(() -> { try { // 构建开始和结束时间字符串 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); String startTimeStr = sdf.format(new Date(video.startTime)); String stopTimeStr = sdf.format(new Date(video.endTime)); // 构建POST请求参数 StringBuilder postData = new StringBuilder(); postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); postData.append("&deviceSerial=").append(URLEncoder.encode(mDeviceSerial, "UTF-8")); postData.append("&channelNo=").append(mCameraNo); postData.append("&protocol=").append(PROTOCOL_EZOPEN); postData.append("&type=2"); // 2表示本地录像回放 postData.append("&startTime=").append(URLEncoder.encode(startTimeStr, "UTF-8")); postData.append("&stopTime=").append(URLEncoder.encode(stopTimeStr, "UTF-8")); postData.append("&quality=1"); // 1-高清 URL url = new URL(PLAYBACK_URL_API); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(10000); conn.setReadTimeout(10000); // 发送请求 OutputStream os = conn.getOutputStream(); os.write(postData.toString().getBytes("UTF-8")); os.flush(); os.close(); // 处理响应 int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); // 解析JSON响应 JSONObject json = new JSONObject(response.toString()); String code = json.optString("code", "0"); if ("200".equals(code)) { JSONObject data = json.getJSONObject("data"); String playbackUrl = data.getString("url"); // 回到主线程播放视频 runOnUiThread(() -> playWithUrl(playbackUrl, video)); } else { String msg = json.optString("msg", "未知错误"); Log.e(TAG, "获取播放地址失败: " + msg); runOnUiThread(() -> Toast.makeText(FanHui.this, "获取播放地址失败: " + msg, Toast.LENGTH_SHORT).show()); } } else { Log.e(TAG, "HTTP错误: " + responseCode); runOnUiThread(() -> Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } conn.disconnect(); } catch (Exception e) { Log.e(TAG, "获取播放地址异常", e); runOnUiThread(() -> Toast.makeText(FanHui.this, "获取播放地址出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } }); } /** * 使用URL播放视频 */ private void playWithUrl(String url, VideoInfo video) { try { // 创建播放器实例 mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // 设置回调处理器 mEZPlayer.setHandler(mHandler); // 设置验证码 mEZPlayer.setPlayVerifyCode(mVerifyCode); // 关联播放视图 if (mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } else { Log.e(TAG, "无法关联播放视图"); Toast.makeText(this, "播放视图未初始化", Toast.LENGTH_SHORT).show(); return; } // 开始播放 mEZPlayer.startPlayback(url); Toast.makeText(this, "开始播放录像: " + video.timeRange, Toast.LENGTH_SHORT).show(); } catch (Exception e) { Log.e(TAG, "播放录像失败", e); Toast.makeText(this, "播放录像失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } private void stopPlayback() { if (mEZPlayer != null) { mEZPlayer.stopPlayback(); mEZPlayer.release(); mEZPlayer = null; } } // 播放器回调处理器 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case EZConstants.EZRealPlayConstants.MSG_REALPLAY_PLAY_SUCCESS: Log.i(TAG, "回放播放成功"); break; case MSG_PLAYBACK_PLAY_SUCCESS: // 使用自定义常量 Log.i(TAG, "URL回放播放成功"); break; case EZConstants.EZRealPlayConstants.MSG_REALPLAY_PLAY_FAIL: case MSG_PLAYBACK_PLAY_FAIL: // 使用自定义常量 Log.e(TAG, "回放播放失败"); BaseException error = (BaseException) msg.obj; int errorCode = error.getErrorCode(); String errorMsg = "播放失败: " + errorCode; if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED) { errorMsg = "需要验证码"; } else if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) { errorMsg = "验证码错误"; } else if (errorCode == ErrorCode.ERROR_TRANSF_ACCESSTOKEN_ERROR) { errorMsg = "accessToken无效"; } Toast.makeText(FanHui.this, errorMsg, Toast.LENGTH_LONG).show(); break; } } }; @Override protected void onPause() { super.onPause(); if (mEZPlayer != null) { mEZPlayer.stopRealPlay(); mEZPlayer.release(); mEZPlayer = null; } } @Override protected void onDestroy() { super.onDestroy(); stopPlayback(); if (mExecutorService != null) { mExecutorService.shutdown(); } } // 录像信息数据结构 private static class VideoInfo { String id; long startTime; long endTime; int recType; String timeRange; } // 列表适配器 private class PlaybackAdapter extends BaseAdapter { @Override public int getCount() { return mVideoList.size(); } @Override public Object getItem(int position) { return mVideoList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = getLayoutInflater().inflate(R.layout.video_item_layout, parent, false); holder = new ViewHolder(); holder.timeTextView = convertView.findViewById(R.id.time_text); holder.durationTextView = convertView.findViewById(R.id.duration_text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } VideoInfo video = mVideoList.get(position); // 计算持续时间(分钟) long durationMinutes = (video.endTime - video.startTime) / (1000 * 60); holder.timeTextView.setText(video.timeRange); holder.durationTextView.setText(durationMinutes + "分钟"); return convertView; } class ViewHolder { TextView timeTextView; TextView durationTextView; } } } 依据上述代码解决报错:Cannot resolve method 'startPlayback(java.lang.String)'
06-26
package com.adayo.service.atsmode.atsview; /** * Copyright (c) 2015 FORYOU GENERAL ELECTRONICS CO.,LTD. All Rights Reserved. */ import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.RectF; import android.hardware.fgecomservice.V1_0.EMessageID; import android.hardware.fgecomservice.V1_0.IFgeComService; import android.hardware.fgecomservice.V1_0.Message; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.widget.ImageView; import android.widget.TextView; import android.widget.VideoView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.lifecycle.LifecycleRegistry; import com.adayo.proxy.infrastructure.sourcemng.Beans.AppConfigType; import com.adayo.proxy.infrastructure.sourcemng.Beans.SourceInfo; import com.adayo.proxy.infrastructure.sourcemng.Control.SrcMngAudioSwitchManager; import com.adayo.proxy.infrastructure.sourcemng.Control.SrcMngSwitchManager; import com.adayo.proxy.infrastructure.sourcemng.Interface.IAdayoAudioFocusChange; import com.adayo.proxy.setting.system.utils.LogUtil; import com.adayo.service.atsmode.R; import com.adayo.service.atsmode.atsconfig.ATSConfig; import com.airbnb.lottie.LottieAnimationView; import com.jakewharton.rxbinding4.view.RxView; import com.jeremyliao.liveeventbus.LiveEventBus; import org.jetbrains.annotations.NotNull; import org.json.JSONObject; import java.io.IOException; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; import autodispose2.AutoDispose; import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider; import static com.adayo.service.atsmode.atsconfig.ATSConfig.ATS_MODE_WADE; import static com.adayo.service.atsmode.atsconfig.ATSConfig.ATS_TAG; import static com.adayo.service.atsmode.atsconfig.ATSConfig.CONFIG_CAR_MODEL_B41V; import static com.adayo.service.atsmode.atsconfig.ATSConfig.CONFIG_CAR_MODEL_B41VS; import static com.adayo.service.atsmode.atsconfig.ATSConfig.CONFIG_CAR_MODEL_B60VS; import static com.adayo.service.atsmode.atsconfig.ATSConfig.EVENT_ATS_RESET_COUNTER; import static com.adayo.service.atsmode.atsconfig.ATSConfig.FLAG_OPERATE_UPDATE; import static com.adayo.service.atsmode.atsconfig.ATSConfig.MODEL_CONFIGURATION_VERSION_C; import static com.adayo.service.atsmode.atsconfig.ATSConfig.MODEL_CONFIGURATION_VERSION_P; /** * @ClassName: ATSContainerView * @Description: ATS驾驶模式容器view * @Author: yongqiang.wen@foryouge.com.cn * @CreateDate: 2022/12/13 14:03 */ public class ATSContainerView extends ConstraintLayout { SvgPathView svgPathView = null; VideoView videoView = null; TextView atsModeName = null; View atsToastTipsContainer = null; View atsToastIconContainer = null; ImageView atsLoadingView = null; ImageView atsErrorIcon = null; TextView atsToastTips = null; LottieAnimationView lottieAnimationView = null; String currentPathName = ""; String requestedModeName = ""; String confirmModeName = ""; private OnATSViewCloseListener closeListener = null; // 生命周期感知注册 private LifecycleRegistry lifecycleRegistry = null; // ATS驾驶模式ID对应关系表 private HashMap<Integer, String> pathIds = new HashMap<>(); // 用于查找驾驶模式中英文对照 // private HashMap<String, String> pathMaps = new HashMap<>(); // 加载下一个模式,用于判断是否播放剩余的50%动效 private boolean notYetSwitchNext = false; // 信号值标记,用于记录是否有信号过来 private int operateFlag; // 上一个模式记录 private String previousModePathName; // ATS驾驶模式关闭按钮 private ImageView atsClose; // 型号配置字 int carModelConfig = -1; // 模式配置字 int configuration = -1; // 是否有效信号 private boolean isValidSignal = false; private IFgeComService mIFgeComService = null; private MediaPlayer mediaPlayer; public ATSContainerView(Context context) { this(context, null); } public ATSContainerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ATSContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void initATSView() { // 初始化子View initChildView(); // 初始化视频播放器 initVideoView(); // 初始化连接服务 initComService(); } private void initComService() { if (mIFgeComService == null) { try { mIFgeComService = IFgeComService.getService(); } catch (RemoteException e) { e.printStackTrace(); } } } public int getCarModelConfig() { return carModelConfig; } public void setCarModelConfig(int carModelConfig) { this.carModelConfig = carModelConfig; initATSSrcData(carModelConfig); if (svgPathView != null && !svgPathView.hasInitialize()) { svgPathView.init(); } LogUtil.e(ATSConfig.ATS_TAG, "ATSContainerView init carModelConfig byte value is ====>>" + carModelConfig); } public void initATSSrcData(int carModelConfig) { if (carModelConfig == CONFIG_CAR_MODEL_B41V) { // 生成41V模式数据 genAtsModeData(); } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS || carModelConfig == CONFIG_CAR_MODEL_B41VS) { // 生成60VS模式数据 gen60VAtsModeData(); } } public int getConfiguration() { return configuration; } public void setConfiguration(int configuration) { this.configuration = configuration; LogUtil.e(ATSConfig.ATS_TAG, "ATSContainerView init configuration byte value is ====>>" + configuration); } private void initChildView() { svgPathView = getRootView().findViewById(R.id.svg_view); videoView = getRootView().findViewById(R.id.video_view); atsModeName = getRootView().findViewById(R.id.tv_ats_mode_name); atsClose = getRootView().findViewById(R.id.iv_ats_close); atsToastTipsContainer = getRootView().findViewById(R.id.toast_ats_mode_enter_tips); atsToastIconContainer = getRootView().findViewById(R.id.view_icon_container); atsLoadingView = getRootView().findViewById(R.id.iv_toast_loading); atsErrorIcon = getRootView().findViewById(R.id.iv_toast_icon); atsToastTips = getRootView().findViewById(R.id.tv_toast_msg); RxView.clicks(atsClose) .throttleFirst(500, TimeUnit.MILLISECONDS) .to(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleRegistry))) .subscribe(unit -> closeListener.onClose()); } public OnATSViewCloseListener getCloseListener() { return closeListener; } public void setCloseListener(OnATSViewCloseListener closeListener) { this.closeListener = closeListener; } int showMode = -1; private void initVideoView() { videoView.setOnCompletionListener(MediaPlayer::stop); videoView.setOnPreparedListener(mp -> { // 设置MediaPlayer不需要音频焦点 // mp.setAudioAttributes(new AudioAttributes.Builder() // .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) // .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) // .build()); // 判断切换模式动效View是否显示,进行隐藏 if (lottieAnimationView.getVisibility() == View.VISIBLE) { lottieAnimationView.setVisibility(View.INVISIBLE); atsModeName.setVisibility(View.VISIBLE); atsModeName.setText(getATSModeName(currentPathName)); } }); } private void handlingSpecialModeSignals() { showMode = -1; switch (currentPathName) { case "wad": if (carModelConfig == CONFIG_CAR_MODEL_B41V) { showMode = ATSConfig.ATS_MODE_WADE; } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS || carModelConfig == CONFIG_CAR_MODEL_B41VS) { showMode = ATSConfig.ATS60_MODE_WAD; } break; case "rock": if (carModelConfig == CONFIG_CAR_MODEL_B41V) { showMode = ATSConfig.ATS_MODE_ROCK; } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS || carModelConfig == CONFIG_CAR_MODEL_B41VS) { showMode = ATSConfig.ATS60_MODE_ROCK; } break; } if (showMode == ATSConfig.ATS_MODE_ROCK) { LogUtil.e(ATS_TAG, "ready to send enter rock mode signal to QNX"); } LiveEventBus.get(EVENT_ATS_RESET_COUNTER, Integer.class).post(showMode); // postDelayed(() -> { // if (showMode == ATSConfig.ATS_MODE_WADE && isValidSignal) { // String wadeTips = getContext().getString(R.string.tips_enter_the_water_depth_detection); // showModeEnterToast(wadeTips, true); // } else if (showMode == ATSConfig.ATS_MODE_ROCK && isValidSignal) { // String rockTips = getContext().getString(R.string.tips_enter_the_transparent_chassis); // showModeEnterToast(rockTips, true); // } // removeCallbacks(hideCustomToastRunnable); // postDelayed(hideCustomToastRunnable, 3000); // }, 2000); } private void hideCustomToast() { if (atsToastTipsContainer.getVisibility() == VISIBLE) { atsToastTipsContainer.setVisibility(GONE); } } private ObjectAnimator rotationAnimator = null; public void showModeEnterToast(String tips, boolean showLoading) { if (atsToastTipsContainer.getVisibility() == GONE) { atsToastTipsContainer.setVisibility(VISIBLE); } if (showLoading) { atsToastIconContainer.setVisibility(VISIBLE); atsLoadingView.setVisibility(VISIBLE); atsErrorIcon.setVisibility(GONE); // 创建旋转动画 if (rotationAnimator == null) { rotationAnimator = ObjectAnimator.ofFloat(atsLoadingView, "rotation", 0f, 360f); rotationAnimator.setDuration(1000); // 动画持续时间,单位为毫秒 rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE); // 无限重复 rotationAnimator.setInterpolator(new LinearInterpolator()); // 线性插值器,使旋转平滑 // 启动动画 rotationAnimator.start(); } } else { atsToastIconContainer.setVisibility(GONE); atsLoadingView.setVisibility(GONE); } atsToastTipsContainer.setBackground(getResources().getDrawable(R.drawable.customize_toast_bg)); atsToastTips.setTextColor(getResources().getColor(R.color.drawable_dialog_text_color)); atsToastTips.setText(tips); } public void showErrorToast() { removeCallbacks(hideCustomToastRunnable); if (atsToastTipsContainer.getVisibility() == GONE) { atsToastTipsContainer.setVisibility(VISIBLE); } atsToastIconContainer.setVisibility(VISIBLE); atsErrorIcon.setVisibility(VISIBLE); atsLoadingView.setVisibility(GONE); atsToastTips.setText(getContext().getString(R.string.fail)); } /** * @param * @return void * @method observeRequestedModeDisplay * @description 切换模式回调 * @date: 2022/12/13 14:56 * @author: yongqiang.wen@foryouge.com.cn */ public void observeRequestedModeDisplay(int signal) { if (videoView != null && videoView.isPlaying()) { videoView.stopPlayback(); videoView.setBackground(getBackground()); setVideoViewVisibility(GONE); } setSvgPathViewVisibility(VISIBLE); requestedModeName = pathIds.get(signal); isValidSignal = false; if (checkInvalidSignal(signal)) return; isValidSignal = true; LogUtil.e(ATS_TAG, "The ATS mode requested signal has been received, current signal ===>> " + signal); requestedModeName = pathIds.get(signal); currentPathName = requestedModeName; // 如果上一个模式记录为空,则记录本次切换的模式路径 if (TextUtils.isEmpty(previousModePathName)) previousModePathName = currentPathName; LogUtil.d(ATS_TAG, "The ATS mode requested signal has been received, current mode ===>> " + currentPathName); atsModeName.setText(""); if (videoView.isPlaying()) { videoView.pause(); } atsModeName.setVisibility(View.GONE); if (lottieAnimationView != null && lottieAnimationView.isAnimating()) { // 处理当动效未结束,快速切换的问题 notYetSwitchNext = false; if (svgPathView.getSelect() != null) { previousModePathName = svgPathView.getSelect().getId(); } } if (signal >= 0 && signal < 12 || signal == 15) { // 由于目前41和60都是同样的模式范围,如果后续有变动,此处需要变更 showSwitchTips(); } removeCallbacks(hideCustomToastRunnable); switchNextMode(currentPathName); } private void showSwitchTips() { // atsToastTips.setVisibility(VISIBLE); String modeName = getATSModeName(currentPathName); String tipsTxt = getContext().getString(R.string.tips_switch_mode_txt, modeName); LogUtil.e(ATS_TAG, "The ATS mode requested signal has been received, current mode ===>> " + currentPathName); LogUtil.e(ATS_TAG, "The ATS mode requested signal has been received, current mode ===>> " + modeName + ", tipsTxt ===>>" + tipsTxt); showModeEnterToast(tipsTxt, false); // atsModeEnterTips.setText(tipsTxt); } /** * ATS岩石模式切换 发送给qnx * * @param rockModeState state */ public void updateRockModeStatus(int rockModeState) { LogUtil.i(ATS_TAG, "rockModeState = " + rockModeState); String id = "atsstatus"; String sub = ""; //装入json try { JSONObject command = new JSONObject(); command.put("id", id); command.put("sub", sub); JSONObject content = new JSONObject(); content.put("status", rockModeState); JSONObject all = new JSONObject(); all.put("command", command); all.put("content", content); String all_message = all.toString(); // LogUtil.i(ATS_TAG, "all_message = " + all_message); Message msg = new Message(); //看协议ID:device_070,然后看源码文件types.hal查看ID msg.id = EMessageID.MSG_DEVICE_ATSSTATUS; msg.content = all_message; LogUtil.i(ATS_TAG, "msg send = " + msg.content); if (mIFgeComService != null) { mIFgeComService.fgeSendMessage2ComService(msg); } } catch (Exception e) { LogUtil.e(ATS_TAG, "e = " + e.toString()); } } /** * @param * @return void * @method observeConfirmedModeDisplay * @description 确认模式回调 * @date: 2022/12/13 14:57 * @author: yongqiang.wen@foryouge.com.cn */ public boolean observeConfirmedModeDisplay(int signal) { if (checkInvalidSignal(signal)) return false; confirmModeName = pathIds.get(signal); atsModeName.setVisibility(View.GONE); LogUtil.d(ATS_TAG, "The ATS mode confirmation signal has been received,current mode ===>>" + getATSModeName(currentPathName)); if (TextUtils.equals(requestedModeName, confirmModeName)) { post(() -> runSwitchModeDisplayVideo(confirmModeName)); // atsClose.setVisibility(VISIBLE); String modeName = getATSModeName(confirmModeName); LogUtil.e(ATS_TAG, "setting current modeName ==>> " + modeName); atsModeName.setText(modeName); atsModeName.setVisibility(View.VISIBLE); queryOtherCMD(); return true; } return false; } public void setSvgPathViewVisibility(int visibility) { LogUtil.e(ATS_TAG, "setSvgPathViewVisibility called, visibility = " + visibility); if (svgPathView != null) { svgPathView.setVisibility(visibility); } if (atsClose != null) { atsClose.setVisibility(visibility); } if (atsModeName != null && atsModeName.getVisibility() == VISIBLE) { atsModeName.setVisibility(INVISIBLE); } } public void setVideoViewVisibility(int visibility) { LogUtil.e(ATS_TAG, "setVideoViewVisibility called, visibility = " + visibility); if (videoView != null) { videoView.setVisibility(visibility); } if (atsClose != null) { atsClose.setVisibility(visibility); } if (atsModeName != null) { atsModeName.setVisibility(visibility); } } private boolean checkInvalidSignal(int signal) { if (videoView.getVisibility() == VISIBLE) { setVideoViewVisibility(GONE); } if (carModelConfig == CONFIG_CAR_MODEL_B41V) { return signal == ATSConfig.ATS_MODE_RESERVED1 || signal == ATSConfig.ATS_MODE_RESERVED2; // if (signal == ATSConfig.ATS_MODE_FAULT || signal == ATSConfig.ATS_MODE_NOT_INITIALIZED) { // showErrorToast(); // return true; // } } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS || carModelConfig == CONFIG_CAR_MODEL_B41VS) { return signal == ATSConfig.ATS60_MODE_SINGLE_PEDAL || signal == ATSConfig.ATS60_MODE_RAPIDLY || signal == ATSConfig.ATS60_MODE_RESERVED1 || signal == ATSConfig.ATS60_MODE_NOT_INITIALIZED; // if (signal == ATSConfig.ATS60_MODE_FAULT || signal == ATSConfig.ATS60_MODE_NOT_INITIALIZED) { // showErrorToast(); // return true; // } } return false; } /** * 特定模式下需要额外处理开启某功能,比如涉水模式要切换到涉水信息界面 */ private void queryOtherCMD() { if (!TextUtils.equals(currentPathName, pathIds.get(ATSConfig.ATS_MODE_ROCK))) { // 非岩石模式需要给QNX发送退出岩石模式信号 LogUtil.e(ATS_TAG, "ready to send exit rock mode signal to QNX"); updateRockModeStatus(ATSConfig.EXTRA_ROCK_STATE_EXIT); } // if (TextUtils.equals(currentPathName, pathIds.get(ATS_MODE_WADE))) { // 涉水模式 需要打开涉水页面 // Map<String, String> map = new HashMap<>(); // map.put("source", "carsetting"); // map.put("dest", "wddc"); // SourceInfo sourceInfo = new SourceInfo("com.adayo.app.bcm", map, // AppConfigType.SourceSwitch.APP_ON.getValue(), // AppConfigType.SourceType.UI_AUDIO.getValue()); // SrcMngSwitchManager.getInstance().requestSwitchApp(sourceInfo); // } } public void resetPreviousModePathName() { previousModePathName = ""; } /** * 获取当前模式 * @param currentMode */ private String getATSModeName(String currentMode) { if (carModelConfig == CONFIG_CAR_MODEL_B60VS) { // 北汽B60VS车型 if (currentMode.equals("snow")) { currentMode = "snow_60"; } } int modeRes = getResources().getIdentifier(currentMode, "string", getContext().getPackageName()); String currentName = ""; if (modeRes != 0) { currentName = getContext().getString(modeRes); } return currentName; } /** * @param * @return void * @method genAtsModeData * @description 生成ATS驾驶模式数据关系表,用于切换不同模式定位path * @date: 2022/11/28 9:43 * @author: yongqiang.wen@foryouge.com.cn */ private void genAtsModeData() { // 生成ATSMode映射关系表 pathIds.clear(); pathIds.put(ATSConfig.ATS_MODE_ECO, "eco"); pathIds.put(ATSConfig.ATS_MODE_COMFORT, "conf"); pathIds.put(ATSConfig.ATS_MODE_AUTO, "auto"); pathIds.put(ATSConfig.ATS_MODE_SNOW, "snow"); pathIds.put(ATSConfig.ATS_MODE_DEEP_SNOW, "deep_snow"); pathIds.put(ATSConfig.ATS_MODE_CROSS, "cross"); pathIds.put(ATSConfig.ATS_MODE_SAND, "sand"); pathIds.put(ATSConfig.ATS_MODE_MUD, "mud"); pathIds.put(ATSConfig.ATS_MODE_SLIP_MUD, "mud"); pathIds.put(ATSConfig.ATS_MODE_ROCK, "rock"); pathIds.put(ATSConfig.ATS_MODE_WADE, "wad"); pathIds.put(ATSConfig.ATS_MODE_SPORT, "sport"); // 系统未初始化的时候不显示任何模式 pathIds.put(ATSConfig.ATS_MODE_NOT_INITIALIZED, ""); // 当系统连接失败或者错误时,默认显示‘舒适’模式 pathIds.put(ATSConfig.ATS_MODE_FAULT, "conf"); } /** * @param * @return void * @method genAtsModeData * @description 生成ATS驾驶模式数据关系表,用于切换不同模式定位path * @date: 2023/6/12 9:43 * @author: yongqiang.wen@foryouge.com.cn */ private void gen60VAtsModeData() { // 生成ATSMode映射关系表 pathIds.clear(); pathIds.put(ATSConfig.ATS60_MODE_ECO, "eco"); pathIds.put(ATSConfig.ATS60_MODE_COMFORT, "conf"); pathIds.put(ATSConfig.ATS60_MODE_LIGHT_SNOW, "snow"); pathIds.put(ATSConfig.ATS60_MODE_DEEP_SNOW, "snow"); // pathIds.put(ATSConfig.ATS60_MODE_SINGLE_PEDAL, "single_pedal"); pathIds.put(ATSConfig.ATS60_MODE_RAPIDLY, "rapidly"); pathIds.put(ATSConfig.ATS60_MODE_THROUGH, "cross"); pathIds.put(ATSConfig.ATS60_MODE_SAND, "sand"); pathIds.put(ATSConfig.ATS60_MODE_DEEP_MUD, "mud"); pathIds.put(ATSConfig.ATS60_MODE_SHALLOW_MUD, "mud"); pathIds.put(ATSConfig.ATS60_MODE_ROCK, "rock"); pathIds.put(ATSConfig.ATS60_MODE_WAD, "wad"); pathIds.put(ATSConfig.ATS60_MODE_SPORT, "sport"); // 系统未初始化的时候不显示任何模式 pathIds.put(ATSConfig.ATS_MODE_NOT_INITIALIZED, ""); // 当系统连接失败或者错误时,默认显示‘舒适’模式 pathIds.put(ATSConfig.ATS_MODE_FAULT, "conf"); } private void switchNextMode(String pathName) { if (TextUtils.isEmpty(pathName)) return; if (lottieAnimationView == null) { ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); lottieAnimationView = new LottieAnimationView(getContext()); // lottieAnimationView.addAnimatorListener(animatorListener); lottieAnimationView.addAnimatorUpdateListener(animation -> lottieAnimationView.setProgress(0.5f)); addView(lottieAnimationView, layoutParams); } lottieAnimationView.setVisibility(View.VISIBLE); runLottieAnim(pathName); } private final Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { LogUtil.d(ATS_TAG, "animation start!" + animation.toString()); } @Override public void onAnimationEnd(Animator animation) { // 当上一个记录的模式路径与当前不一致时代表模式已经切换, // 需要先播放上一个路径消失的动效再播放新模式的加载动效 // if (!TextUtils.equals(currentPathName, previousModePathName)) { // previousModePathName = currentPathName; // switchNextMode(currentPathName); // } else if (notYetSwitchNext) { // lottieAnimationView.setMinAndMaxProgress(0.5f, 1); // notYetSwitchNext = false; // } LogUtil.d(ATS_TAG, "animation end!"); } @Override public void onAnimationCancel(Animator animation) { LogUtil.d(ATS_TAG, "animation cancel!"); } @Override public void onAnimationRepeat(Animator animation) { LogUtil.d(ATS_TAG, "animation repeat!"); } }; private void runSwitchModeDisplayVideo(String pathName) { // 切换模式需要让未播放的视频暂停 videoView.pause(); String currentPathName = ""; if (carModelConfig == CONFIG_CAR_MODEL_B41V) { // 北汽B41V车型 currentPathName = getB41VModelPath(pathName); } else if (carModelConfig == CONFIG_CAR_MODEL_B41VS) { // 北汽B41VS车型 currentPathName = getB41VSModelPath(pathName); } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS) { // 北汽B60VS车型 currentPathName = getB60VSModelPath(pathName); } if (TextUtils.isEmpty(currentPathName)) return; svgPathView.setPathSelect(pathName); // 根据path name通过资源文件获取对应的动效视频路径 int videoSrc = getResources().getIdentifier(currentPathName, "raw", getContext().getPackageName()); String videoPath = "android.resource://" + getContext().getPackageName() + "/" + videoSrc; post(() -> videoView.setVideoPath(videoPath)); LogUtil.d(ATS_TAG, "current Video path is " + videoPath + ",currentPathName is " + currentPathName); if (videoSrc == 0) { return; } post(() -> { String tipsStr = getTipsStringByConfig(); if (!TextUtils.equals(confirmModeName, pathIds.get(ATSConfig.ATS60_MODE_RAPIDLY))) { // 极速模式没有提示文案 showModeEnterToast(tipsStr, false); removeCallbacks(hideCustomToastRunnable); postDelayed(hideCustomToastRunnable, 5000); } else { hideCustomToast(); } videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE); // SrcMngAudioSwitchManager.getInstance().requestAdayoAudioFocus(3, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, "com.adayo.service.atsmode", mFocusChange,getContext()); videoView.setBackground(null); //设置音频策略跟随ktv通道 videoView.setAudioAttributes(new AudioAttributes.Builder() .setUsage(43) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build()); // videoView.setAudioAttributes(new AudioAttributes.Builder() // .setLegacyStreamType(3) // .build()); videoView.start(); setSvgPathViewVisibility(INVISIBLE); setVideoViewVisibility(VISIBLE); handlingSpecialModeSignals(); // mediaPlayer = new MediaPlayer(); // int videoSrc2 = getResources().getIdentifier("deepsnow", "raw", getContext().getPackageName()); // try { // mediaPlayer.setDataSource(getContext(), Uri.parse("android.resource://" + getContext().getPackageName() + "/" + videoSrc2)); // } catch (IOException e) { // e.printStackTrace(); // } // // mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() // .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) // .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) // .build()); // // mediaPlayer.prepareAsync(); // mediaPlayer.setLooping(true); // mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { // @Override // public void onPrepared(MediaPlayer mediaPlayer) { // mediaPlayer.start(); // } // }); // // // // new Handler().postDelayed(new Runnable() { // @Override // public void run() { // mediaPlayer.stop(); // mediaPlayer.reset(); // } // },7000); }); } private String getTipsStringByConfig() { int tipsRes = 0; String tipsStr = ""; if (carModelConfig == CONFIG_CAR_MODEL_B41V) { // 北汽B41V车型 tipsRes = getResources().getIdentifier("tips41_" + confirmModeName, "string", getContext().getPackageName()); } else if (carModelConfig == CONFIG_CAR_MODEL_B41VS) { if (TextUtils.equals(confirmModeName, pathIds.get(ATSConfig.ATS60_MODE_RAPIDLY))) { return ""; } // tipsRes = getResources().getIdentifier("tips41vs_" + confirmModeName, "string", getContext().getPackageName()); tipsRes = getResources().getIdentifier("tips60_" + confirmModeName, "string", getContext().getPackageName()); } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS) { if (TextUtils.equals(confirmModeName, pathIds.get(ATSConfig.ATS60_MODE_RAPIDLY))) { return ""; } tipsRes = getResources().getIdentifier("tips60_" + confirmModeName, "string", getContext().getPackageName()); } tipsStr = getContext().getString(tipsRes); return tipsStr; } @NotNull private String getB41VModelPath(String pathName) { String currentPathName; // 根据配置字来匹配展示的动效文件 if (configuration == MODEL_CONFIGURATION_VERSION_C) { LogUtil.d(ATS_TAG, "current model configuration is C Version"); currentPathName = pathName + "_c"; } else if (configuration == MODEL_CONFIGURATION_VERSION_P) { LogUtil.d(ATS_TAG, "current model configuration is P Version"); currentPathName = pathName + "_p"; } else { LogUtil.d(ATS_TAG, "can not require current model configuration"); currentPathName = pathName + "_p"; } return currentPathName; } @NotNull private String getB60VSModelPath(String pathName) { String currentPathName; // 根据配置字来匹配展示的动效文件 currentPathName = pathName + "_60vs"; return currentPathName; } @NotNull private String getB41VSModelPath(String pathName) { String currentPathName; // 鏍规嵁閰嶇疆瀛楁潵鍖归厤灞曠ず鐨勫姩鏁堟枃浠? currentPathName = pathName + "_41vs"; return currentPathName; } private final Runnable hideCustomToastRunnable = this::hideCustomToast; private void runLottieAnim(String pathName) { // 播放切换动效 RectF rectF = svgPathView.getSelectRectF(pathName); if (lottieAnimationView != null && rectF != null) { // // 切换其他模式要先播放之前模式的隐藏动效 // if (!TextUtils.equals(previousModePathName, pathName) && lottieAnimationView.getVisibility() == VISIBLE) { // lottieAnimationView.playAnimation(); // return; // } // 设置lottieView宽高 lottieAnimationView.getLayoutParams().width = (int) rectF.width(); lottieAnimationView.getLayoutParams().height = (int) rectF.height(); // 设置lottieView对应的位置坐标 lottieAnimationView.setX(rectF.centerX() - rectF.width() / 2); lottieAnimationView.setY(rectF.centerY() - rectF.height() / 2); String animSrc = getStringLottiePathSrc(pathName); if (TextUtils.isEmpty(animSrc)) return; LogUtil.d(ATS_TAG, "lottie res file path :" + animSrc); lottieAnimationView.setAnimation(animSrc); lottieAnimationView.setRepeatCount(0); lottieAnimationView.setSpeed(0f); lottieAnimationView.setFrame(16); // if (!notYetSwitchNext) { // // 由于UI给的动效文件是完整的动画过程,50%是动效加载并显示的过程,剩余的50% // lottieAnimationView.setMinAndMaxProgress(0.5f, 0.5f); // notYetSwitchNext = true; // } lottieAnimationView.playAnimation(); operateFlag = FLAG_OPERATE_UPDATE; } } @NotNull private String getStringLottiePathSrc(String pathName) { // 处理多语言的情况,目前只有中英文 Locale locale = getResources().getConfiguration().locale; String language = locale.getLanguage(); LogUtil.d(ATS_TAG, "current system language is: " + language); String animSrc = null; if (carModelConfig == CONFIG_CAR_MODEL_B41V) { // 根据path加载对应的Lottie动画资源文件 if (TextUtils.equals(language, "en")) { animSrc = pathName + "41_en.json"; } else { animSrc = pathName + "41.json"; } } else if (carModelConfig == CONFIG_CAR_MODEL_B41VS) { // 根据path加载对应的Lottie动效资源文件 if (TextUtils.equals(language, "en")) { animSrc = pathName + "60_en.json"; } else { animSrc = pathName + "60.json"; } } else if (carModelConfig == CONFIG_CAR_MODEL_B60VS) { // 根据path加载对应的Lottie动画资源文件 if (TextUtils.equals(language, "en")) { animSrc = pathName + "60_en.json"; } else { animSrc = pathName + "60.json"; } } return animSrc; } public LifecycleRegistry getLifecycleRegistry() { return lifecycleRegistry; } public void setLifecycleRegistry(LifecycleRegistry lifecycleRegistry) { this.lifecycleRegistry = lifecycleRegistry; } public interface OnATSViewCloseListener { void onClose(); } private static IAdayoAudioFocusChange mFocusChange = new IAdayoAudioFocusChange() { @Override public void onAdayoAudioOnGain() { } @Override public void onAdayoAudioOnLoss() { } @Override public void onAdayoAudioLossTransient() { } @Override public void onAdayoAudioLossTransientCanDuck() { } }; }
最新发布
08-06
package com.videogo.ui.login; import android.content.res.Configuration; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.videogo.exception.BaseException; import com.videogo.exception.ErrorCode; import com.videogo.openapi.EZConstants; import com.videogo.openapi.EZOpenSDK; import com.videogo.openapi.EZPlayer; import ezviz.ezopensdk.R; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.LinearLayout; import android.widget.ImageButton; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Handler.Callback { private static final String TAG = "EZPreview"; private static final int MSG_VIDEO_SIZE_CHANGED = 1; private static final int MSG_REALPLAY_PLAY_SUCCESS = 2001; private static final int MSG_REALPLAY_PLAY_FAIL = 2002; private static final int MSG_SHOW_STREAM_TYPE = 3001; // 新增消息类型 // 接收的参数键 private static final String KEY_APPKEY = "appkey"; private static final String KEY_SERIAL = "serial"; private static final String KEY_VERIFYCODE = "VerifyCode"; private static final String KEY_ACCESSTOKEN = "accessToken"; private static final String KEY_CAMERANO = "cameraNo"; private boolean mIsPlaying = false; private EZPlayer mEZPlayer; private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private ProgressBar mLiveProgressBar; private RelativeLayout mRlControl; private LinearLayout mLlHc; private ImageButton mIbRotate2; // 从Intent中获取的参数 private String mAppKey; private String mDeviceSerial; private String mVerifyCode; private String mAccessToken; private int mCameraNo = 0; // 默认通道号0 private Handler mHandler; private boolean mP2PEnabled = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activitymain); mHandler = new Handler(this); // 1. 从Intent中获取参数 extractParametersFromIntent(); // 2. 初始化UI initUI(); initOrientationSensitiveViews(); View fanHui = findViewById(R.id.back); fanHui.setOnClickListener(v -> finish()); // 3. 初始化SDK并创建播放器 initSDKAndCreatePlayer(); } private void initOrientationSensitiveViews() { mLiveProgressBar = findViewById(R.id.liveProgressBar); mRlControl = findViewById(R.id.rl_control); mLlHc = findViewById(R.id.ll_hc); mIbRotate2 = findViewById(R.id.ib_rotate2); // 初始状态显示加载 if (mLiveProgressBar != null) { mLiveProgressBar.setVisibility(View.VISIBLE); } // 初始根据方向更新UI updateLayoutByOrientation(); } @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); // 方向变化时重新初始化视图 initOrientationSensitiveViews(); updateLayoutByOrientation(); } @Override protected void onResume() { super.onResume(); if (mLiveProgressBar != null) { if (mIsPlaying) { mLiveProgressBar.setVisibility(View.GONE); } else { mLiveProgressBar.setVisibility(View.VISIBLE); } } } private void updateLayoutByOrientation() { int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_PORTRAIT) { // 竖屏模式 mRlControl.setVisibility(View.VISIBLE); mLlHc.setVisibility(View.GONE); mIbRotate2.setVisibility(View.GONE); } else { // 横屏模式 mRlControl.setVisibility(View.GONE); mLlHc.setVisibility(View.VISIBLE); mIbRotate2.setVisibility(View.VISIBLE); } } /** * 从Intent中提取传递的参数 */ private void extractParametersFromIntent() { Bundle extras = getIntent().getExtras(); if (extras != null) { mAppKey = extras.getString(KEY_APPKEY, ""); mDeviceSerial = extras.getString(KEY_SERIAL, ""); mVerifyCode = extras.getString(KEY_VERIFYCODE, ""); mAccessToken = extras.getString(KEY_ACCESSTOKEN, ""); mCameraNo = extras.getInt(KEY_CAMERANO, 0); Log.d(TAG, "Received parameters:"); Log.d(TAG, "AppKey: " + mAppKey); Log.d(TAG, "DeviceSerial: " + mDeviceSerial); Log.d(TAG, "VerifyCode: " + mVerifyCode); Log.d(TAG, "AccessToken: " + mAccessToken); Log.d(TAG, "CameraNo: " + mCameraNo); } else { Log.e(TAG, "No parameters received from intent"); // 如果没有参数,可以显示错误信息并退出 finish(); } } /** * 初始化UI组件 */ private void initUI() { mSurfaceView = findViewById(R.id.realplay_sv); if (mSurfaceView != null) { mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); } else { Log.e(TAG, "SurfaceView not found with ID realplay_sv"); } } /** * 初始化SDK并创建播放器 */ private void initSDKAndCreatePlayer() { try { // 1. 初始化SDK EZOpenSDK.initLib(getApplication(), mAppKey); EZOpenSDK.getInstance().setAccessToken(mAccessToken); // +++ 开启P2P取流方式 +++ EZOpenSDK.enableP2P(true); // 开启P2P取流 Log.d(TAG, "P2P取流已启用"); mP2PEnabled = true; // 2. 创建播放器 createPlayer(); } catch (Exception e) { Log.e(TAG, "SDK初始化失败", e); Toast.makeText(this, "SDK初始化失败", Toast.LENGTH_SHORT).show(); } } /** * 创建播放器并开始播放 */ private void createPlayer() { try { // 1. 创建播放器实例 mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // 2. 配置播放器 mEZPlayer.setHandler(mHandler); if (mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } if (mVerifyCode != null && !mVerifyCode.isEmpty()) { mEZPlayer.setPlayVerifyCode(mVerifyCode); } // 3. 开始播放 mEZPlayer.startRealPlay(); mIsPlaying = true; // 标记为正在播放 } catch (Exception e) { Log.e(TAG, "Player creation failed", e); mIsPlaying = false; // 标记为未播放 } } // 处理屏幕旋转按钮点击 public void changeScreen(View view) { Log.d(TAG, "Change screen orientation requested"); int currentOrientation = getResources().getConfiguration().orientation; if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } // 更新UI布局 updateLayoutByOrientation(); } // Surface回调接口实现 @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(holder); } } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(null); } } @Override protected void onStop() { super.onStop(); if (mEZPlayer != null) { mEZPlayer.stopRealPlay(); mIsPlaying = false; // 标记为已停止 } } @Override protected void onDestroy() { super.onDestroy(); // +++移除Handler回调避免内存泄漏 +++ if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } if (mEZPlayer != null) { mEZPlayer.release(); mEZPlayer = null; } } // Handler回调处理播放状态 @Override public boolean handleMessage(@NonNull Message msg) { Log.d(TAG, "handleMessage: " + msg.what); switch (msg.what) { case MSG_VIDEO_SIZE_CHANGED: break; case MSG_REALPLAY_PLAY_SUCCESS: Log.i(TAG, "播放成功"); mIsPlaying = true; // +++ 关键修改:获取并显示取流方式 +++ int streamType = mEZPlayer.getStreamFetchType(); String streamTypeName = getStreamTypeName(streamType); Log.d(TAG, "当前取流方式: " + streamTypeName); // 发送消息显示取流方式 Message showMsg = new Message(); showMsg.what = MSG_SHOW_STREAM_TYPE; showMsg.obj = streamTypeName; mHandler.sendMessage(showMsg); runOnUiThread(() -> { if (mLiveProgressBar != null) { mLiveProgressBar.setVisibility(View.GONE); } }); break; case MSG_REALPLAY_PLAY_FAIL: Log.e(TAG, "播放失败"); mIsPlaying = false; runOnUiThread(() -> { if (mLiveProgressBar != null) { mLiveProgressBar.setVisibility(View.VISIBLE); } }); BaseException error = (BaseException) msg.obj; int errorCode = error.getErrorCode(); if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED || errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) { mVerifyCode = "123456"; if (mEZPlayer != null) { mEZPlayer.setPlayVerifyCode(mVerifyCode); mEZPlayer.startRealPlay(); } } else { Log.e(TAG, "播放失败,错误码: " + errorCode); } break; // +++ 新增:显示取流方式 +++ case MSG_SHOW_STREAM_TYPE: String type = (String) msg.obj; Toast.makeText(MainActivity.this, "取流方式: " + type + (mP2PEnabled ? " (P2P已启用)" : ""), Toast.LENGTH_LONG).show(); break; } return true; } private String getStreamTypeName(int type) { switch (type) { case 0: return "流媒体"; case 1: return "P2P"; case 2: return "内网直连"; case 4: return "云存储"; default: return "未知(" + type + ")"; } } } 依据上述代码解决private String getStreamTypeName(int type) { switch (type) { case 0: return "流媒体"; case 1: return "P2P"; case 2: return "内网直连"; case 4: return "云存储"; default: return "未知(" + type + ")"; } }这些代码是什么作用,对页面及功能有什么影响
06-25
package com.videogo.ui.login; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.DatePicker; import android.widget.ImageButton; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.videogo.exception.BaseException; import com.videogo.exception.ErrorCode; import com.videogo.openapi.EZPlayer; import androidx.appcompat.app.AppCompatActivity; import com.videogo.openapi.EZOpenSDK; import ezviz.ezopensdk.R; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FanHui extends AppCompatActivity implements SurfaceHolder.Callback { private static final String TAG = "EZPlayback"; private String mAppKey; private String mDeviceSerial; private String mVerifyCode; private String mAccessToken; private int mCameraNo; private TextView mDateTextView; private int mSelectedYear, mSelectedMonth, mSelectedDay; private static final String KEY_APPKEY = "appkey"; private static final String KEY_SERIAL = "serial"; private static final String KEY_VERIFYCODE = "VerifyCode"; private static final String KEY_ACCESSTOKEN = "accessToken"; private static final String KEY_CAMERANO = "cameraNo"; private static final int MSG_PLAYBACK_PLAY_SUCCESS = 3001; private static final int MSG_PLAYBACK_PLAY_FAIL = 3002; // 更新API地址为云存储专用接口 private static final String VIDEO_BY_TIME_URL = "https://open.ys7.com/api/lapp/v3/videos/by/time"; private static final String PLAYBACK_URL_API = "https://open.ys7.com/api/lapp/device/video/urls"; private ExecutorService mExecutorService; private ListView mListView; private PlaybackAdapter mAdapter; private List<VideoInfo> mVideoList = new ArrayList<>(); // 播放器相关 private EZPlayer mEZPlayer; private SurfaceView mPlaybackSurfaceView; private SurfaceHolder mSurfaceHolder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ez_playback_list_page); // 创建线程池 mExecutorService = Executors.newFixedThreadPool(2); extractParametersFromIntent(); final Calendar calendar = Calendar.getInstance(); mSelectedYear = calendar.get(Calendar.YEAR); mSelectedMonth = calendar.get(Calendar.MONTH); mSelectedDay = calendar.get(Calendar.DAY_OF_MONTH); // 初始化视图 initViews(); // 设置日期显示模块 setupDatePicker(); // 使用MainActivity已初始化的SDK实例 EZOpenSDK.getInstance().setAccessToken(mAccessToken); // 默认加载当天的录像 loadVideosForSelectedDate(); } private void initViews() { // 查找ListView mListView = findViewById(R.id.listView); if (mListView == null) { Log.e(TAG, "ListView not found with ID listView"); return; } // 初始化适配器 mAdapter = new PlaybackAdapter(); mListView.setAdapter(mAdapter); // 设置点击事件 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { VideoInfo video = mVideoList.get(position); playVideo(video); } }); // 获取播放器视图 - 使用SurfaceView mPlaybackSurfaceView = findViewById(R.id.remote_playback_wnd_sv); if (mPlaybackSurfaceView != null) { // 设置SurfaceHolder回调 mSurfaceHolder = mPlaybackSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); } else { Log.e(TAG, "SurfaceView not found with ID remote_playback_wnd_sv"); } } // SurfaceHolder回调方法 @Override public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "Surface created"); // 当Surface创建时,如果有播放器实例,设置SurfaceHolder if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(holder); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "Surface changed: " + width + "x" + height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "Surface destroyed"); // 当Surface销毁时,释放播放器资源 stopPlayback(); } private void setupDatePicker() { mDateTextView = findViewById(R.id.date_text); ImageButton datePickerButton = findViewById(R.id.date_picker_button); updateDateDisplay(); datePickerButton.setOnClickListener(v -> showDatePickerDialog()); } private void updateDateDisplay() { String formattedDate = String.format(Locale.getDefault(), "%d年%02d月%02d日", mSelectedYear, mSelectedMonth + 1, // 月份需要+1 mSelectedDay); mDateTextView.setText(formattedDate); } private void showDatePickerDialog() { final AlertDialog dlg = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog).create(); dlg.show(); Window window = dlg.getWindow(); window.setContentView(R.layout.datepicker_layout); // 设置对话框宽度 WindowManager.LayoutParams lp = window.getAttributes(); lp.width = WindowManager.LayoutParams.MATCH_PARENT; window.setAttributes(lp); // 获取并初始化 DatePicker DatePicker dpPicker = window.findViewById(R.id.dpPicker); // 隐藏不需要的视图 ViewGroup rootView = (ViewGroup) dpPicker.getChildAt(0); if (rootView != null) { ViewGroup childView = (ViewGroup) rootView.getChildAt(0); if (childView != null) { childView.getChildAt(2).setVisibility(View.VISIBLE); // 确保月选择器可见 childView.getChildAt(1).setVisibility(View.VISIBLE); } } dpPicker.init(mSelectedYear, mSelectedMonth, mSelectedDay, null); // 设置按钮事件 RelativeLayout yesButton = window.findViewById(R.id.YES); RelativeLayout noButton = window.findViewById(R.id.NO); yesButton.setOnClickListener(v -> { mSelectedYear = dpPicker.getYear(); mSelectedMonth = dpPicker.getMonth(); mSelectedDay = dpPicker.getDayOfMonth(); updateDateDisplay(); dlg.dismiss(); // 加载新选择的日期的录像 loadVideosForSelectedDate(); }); noButton.setOnClickListener(v -> dlg.dismiss()); } private void extractParametersFromIntent() { Bundle extras = getIntent().getExtras(); if (extras != null) { mAppKey = extras.getString(KEY_APPKEY, ""); mDeviceSerial = extras.getString(KEY_SERIAL, ""); mVerifyCode = extras.getString(KEY_VERIFYCODE, ""); mAccessToken = extras.getString(KEY_ACCESSTOKEN, ""); mCameraNo = extras.getInt(KEY_CAMERANO, 1); // 默认为通道1 Log.d(TAG, "Received parameters:"); Log.d(TAG, "AppKey: " + mAppKey); Log.d(TAG, "DeviceSerial: " + mDeviceSerial); Log.d(TAG, "VerifyCode: " + mVerifyCode); Log.d(TAG, "AccessToken: " + mAccessToken); Log.d(TAG, "CameraNo: " + mCameraNo); } else { Log.e(TAG, "No parameters received from intent"); } } private void loadVideosForSelectedDate() { // 计算开始和结束时间戳 Calendar cal = Calendar.getInstance(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 0, 0, 0); long startTime = cal.getTimeInMillis() / 1000; // 转换为秒级时间戳 cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 23, 59, 59); long endTime = cal.getTimeInMillis() / 1000; // 转换为秒级时间戳 // 发起网络请求获取录像 fetchVideosByTime(startTime, endTime); } private void fetchVideosByTime(long startTime, long endTime) { // 检查Activity状态 if (isFinishing() || isDestroyed()) return; mExecutorService.execute(() -> { try { // 使用V3版本API URL url = new URL(VIDEO_BY_TIME_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(10000); conn.setReadTimeout(10000); // 构建POST数据 StringBuilder postData = new StringBuilder(); postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); postData.append("&deviceSerial=").append(URLEncoder.encode(mDeviceSerial, "UTF-8")); postData.append("&channelNo=").append(mCameraNo); postData.append("&startTime=").append(startTime); postData.append("&endTime=").append(endTime); postData.append("&recType=1"); // 1-云存储录像 OutputStream os = conn.getOutputStream(); os.write(postData.toString().getBytes("UTF-8")); os.flush(); os.close(); // 处理响应 int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); JSONObject json = new JSONObject(response.toString()); String code = json.optString("code", "0"); if ("200".equals(code)) { JSONArray data = json.getJSONArray("data"); List<VideoInfo> videos = parseVideoData(data); // 检查Activity状态再更新UI if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> { mVideoList.clear(); mVideoList.addAll(videos); mAdapter.notifyDataSetChanged(); }); } } else { String msg = json.optString("msg", "未知错误"); Log.e(TAG, "获取录像失败: " + msg); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像失败: " + msg, Toast.LENGTH_SHORT).show()); } } } else { Log.e(TAG, "HTTP错误: " + responseCode); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } } conn.disconnect(); } catch (Exception e) { Log.e(TAG, "获取录像异常", e); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } } }); } private List<VideoInfo> parseVideoData(JSONArray records) throws JSONException { List<VideoInfo> videos = new ArrayList<>(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); for (int i = 0; i < records.length(); i++) { JSONObject record = records.getJSONObject(i); VideoInfo video = new VideoInfo(); video.id = record.optString("id"); // 转换为毫秒时间戳 video.startTime = record.optLong("startTime") * 1000; video.endTime = record.optLong("endTime") * 1000; // 格式化时间显示 Date startDate = new Date(video.startTime); Date endDate = new Date(video.endTime); video.timeRange = sdf.format(startDate) + " - " + sdf.format(endDate); videos.add(video); } return videos; } private void playVideo(VideoInfo video) { // 停止当前播放 stopPlayback(); // 获取播放地址 fetchPlaybackUrl(video); } /** * 获取云存储录像回放地址并播放 */ private void fetchPlaybackUrl(VideoInfo video) { // 检查Activity状态 if (isFinishing() || isDestroyed()) return; mExecutorService.execute(() -> { try { // 构建POST请求参数 StringBuilder postData = new StringBuilder(); postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); postData.append("&deviceSerial=").append(URLEncoder.encode(mDeviceSerial, "UTF-8")); postData.append("&channelNo=").append(mCameraNo); postData.append("&recType=1"); // 1-云存储录像 postData.append("&videoId=").append(URLEncoder.encode(video.id, "UTF-8")); // 传递录像ID URL url = new URL(PLAYBACK_URL_API); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(10000); conn.setReadTimeout(10000); // 发送请求 OutputStream os = conn.getOutputStream(); os.write(postData.toString().getBytes("UTF-8")); os.flush(); os.close(); // 处理响应 int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); // 解析JSON响应 JSONObject json = new JSONObject(response.toString()); String code = json.optString("code", "0"); if ("200".equals(code)) { JSONObject data = json.getJSONObject("data"); JSONArray urls = data.getJSONArray("urls"); if (urls.length() > 0) { String playbackUrl = urls.getJSONObject(0).getString("url"); runOnUiThread(() -> playWithUrl(playbackUrl, video)); } else { runOnUiThread(() -> Toast.makeText(FanHui.this, "未获取到有效播放地址", Toast.LENGTH_SHORT).show()); } } else { String msg = json.optString("msg", "未知错误"); runOnUiThread(() -> Toast.makeText(FanHui.this, "获取播放地址失败: " + msg, Toast.LENGTH_SHORT).show()); } } else { runOnUiThread(() -> Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } conn.disconnect(); } catch (Exception e) { runOnUiThread(() -> Toast.makeText(FanHui.this, "获取播放地址出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } }); } /** * 使用URL播放云存储录像 */ private void playWithUrl(String url, VideoInfo video) { try { // 创建播放器实例 mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // 设置回调处理器 mEZPlayer.setHandler(mHandler); // 设置验证码 mEZPlayer.setPlayVerifyCode(mVerifyCode); // 关联播放视图 if (mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } // 开始播放云存储录像 mEZPlayer.startPlaybackFromCloud(url); Toast.makeText(this, "开始播放录像: " + video.timeRange, Toast.LENGTH_SHORT).show(); } catch (Exception e) { Toast.makeText(this, "播放录像失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } private void stopPlayback() { if (mEZPlayer != null) { mEZPlayer.stopPlayback(); mEZPlayer.release(); mEZPlayer = null; } } // 播放器回调处理器 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { // 使用自定义消息常量 case MSG_PLAYBACK_PLAY_SUCCESS: Log.i(TAG, "回放播放成功"); break; case MSG_PLAYBACK_PLAY_FAIL: Log.e(TAG, "回放播放失败"); BaseException error = (BaseException) msg.obj; int errorCode = error.getErrorCode(); String errorMsg = "播放失败: " + errorCode; if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED) { errorMsg = "需要验证码"; } else if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) { errorMsg = "验证码错误"; } else if (errorCode == ErrorCode.ERROR_TRANSF_ACCESSTOKEN_ERROR) { errorMsg = "accessToken无效"; } Toast.makeText(FanHui.this, errorMsg, Toast.LENGTH_LONG).show(); break; } } }; @Override protected void onPause() { super.onPause(); // 优化线程池管理 if (mExecutorService != null) { mExecutorService.shutdownNow(); mExecutorService = null; } // 停止播放 stopPlayback(); } @Override protected void onResume() { super.onResume(); // 重新创建线程池 if (mExecutorService == null) { mExecutorService = Executors.newFixedThreadPool(2); } } @Override protected void onDestroy() { super.onDestroy(); stopPlayback(); // 使用shutdownNow立即终止线程 if (mExecutorService != null) { mExecutorService.shutdownNow(); } } // 录像信息数据结构 private static class VideoInfo { String id; long startTime; long endTime; String timeRange; } // 列表适配器 private class PlaybackAdapter extends BaseAdapter { @Override public int getCount() { return mVideoList.size(); } @Override public Object getItem(int position) { return mVideoList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = getLayoutInflater().inflate(R.layout.video_item_layout, parent, false); holder = new ViewHolder(); holder.timeTextView = convertView.findViewById(R.id.time_text); holder.durationTextView = convertView.findViewById(R.id.duration_text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } VideoInfo video = mVideoList.get(position); // 计算持续时间(分钟) long durationMinutes = (video.endTime - video.startTime) / (1000 * 60); holder.timeTextView.setText(video.timeRange); holder.durationTextView.setText(durationMinutes + "分钟"); return convertView; } class ViewHolder { TextView timeTextView; TextView durationTextView; } } } 解决上述代码中报错:Cannot resolve method 'startPlaybackFromCloud' in 'EZPlayer'
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值