Android---setContentView(R.layout.activity_other);ERROR 解决

本文介绍了在Android开发中遇到的setContentView方法调用时出现的R文件冲突问题及其解决办法。作者指出,系统自动导入的android.R可能会覆盖自定义的R文件导致错误,并提供了删除错误导入及正确导入自定义R文件的方法。

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

setContentView(R.layout.name);是显示一个布局文件的,我做这个简单的安卓的项目时name处报错。

原因是:      import android.R;      这句是系统自动给你导入的,它会覆盖掉你的gen文件夹中的R文件,所以会出错

解决方法:删掉这一行,并且导入R文件所在包。

还有与此同时你添加的控件例如:ListView myListView = (ListView)findViewById(R.id.other_myListView);    可能出现的错误也就同时解决了。

2025-07-14 19:12:21.243 15048-15048 nativeloader com.example.baiyunmap D Configuring clns-9 for other apk /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk. target_sdk_version=35, uses_libraries=, library_path=/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64:/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk!/lib/x86_64, permitted_path=/data:/mnt/expand:/data/user/0/com.example.baiyunmap 2025-07-14 19:12:21.246 15048-15048 CompatChangeReporter com.example.baiyunmap D Compat change id reported: 202956589; UID 10220; state: ENABLED 2025-07-14 19:12:21.251 15048-15048 ample.baiyunmap com.example.baiyunmap I AssetManager2(0x77841ae44c58) locale list changing from [] to [en-US] 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V Currently set values for: 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V angle_gl_driver_selection_pkgs=[] 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V angle_gl_driver_selection_values=[] 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V com.example.baiyunmap is not listed in per-application setting 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V ANGLE allowlist from config: 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V com.example.baiyunmap is not listed in ANGLE allowlist or settings, returning default 2025-07-14 19:12:21.256 15048-15048 GraphicsEnvironment com.example.baiyunmap V Neither updatable production driver nor prerelease driver is supported. 2025-07-14 19:12:21.270 15048-15048 nativeloader com.example.baiyunmap D Load /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_base_v7_6_5.so using class loader ns clns-9 (caller=/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk!classes3.dex): ok 2025-07-14 19:12:21.301 15048-15048 CompatChangeReporter com.example.baiyunmap D Compat change id reported: 279646685; UID 10220; state: ENABLED 2025-07-14 19:12:21.314 15048-15048 PermissionCheck com.example.baiyunmap E The authManager is: null; the authCallback is: null; the mContext is: null 2025-07-14 19:12:21.319 15048-15048 BaiduApiAuth com.example.baiyunmap I BaiduApiAuth SDK Version:1.0.32 2025-07-14 19:12:21.334 15048-15085 ashmem com.example.baiyunmap E Pinning is deprecated since Android Q. Please use trim or other methods. 2025-07-14 19:12:21.369 15048-15085 CuidBuddyInfoManager com.example.baiyunmap W galaxy lib host missing meta-data,make sure you know the right way to integrate galaxy 2025-07-14 19:12:21.370 15048-15085 CuidBuddyInfoManager com.example.baiyunmap W galaxy lib host missing meta-data,make sure you know the right way to integrate galaxy 2025-07-14 19:12:21.370 15048-15085 CuidBuddyInfoManager com.example.baiyunmap W galaxy lib host missing meta-data,make sure you know the right way to integrate galaxy 2025-07-14 19:12:21.402 15048-15048 PermissionCheck com.example.baiyunmap E The authManager is: null; the authCallback is: null; the mContext is: null 2025-07-14 19:12:21.418 15048-15087 nativeloader com.example.baiyunmap D Load /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libtiny_magic.so using class loader ns clns-9 (caller=/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk!classes4.dex): ok 2025-07-14 19:12:21.493 15048-15048 PermissionCheck com.example.baiyunmap E permission check result is: -11 2025-07-14 19:12:21.509 15048-15090 DisplayManager com.example.baiyunmap I Choreographer implicitly registered for the refresh rate. 2025-07-14 19:12:21.515 15048-15048 ample.baiyunmap com.example.baiyunmap I AssetManager2(0x77841ae4e578) locale list changing from [] to [en-US] 2025-07-14 19:12:21.532 15048-15048 CompatChangeReporter com.example.baiyunmap D Compat change id reported: 309578419; UID 10220; state: ENABLED 2025-07-14 19:12:21.534 15048-15048 DesktopModeFlags com.example.baiyunmap D Toggle override initialized to: OVERRIDE_UNSET 2025-07-14 19:12:21.536 15048-15090 EGL_emulation com.example.baiyunmap I Opening libGLESv1_CM_emulation.so 2025-07-14 19:12:21.537 15048-15090 EGL_emulation com.example.baiyunmap I Opening libGLESv2_emulation.so 2025-07-14 19:12:21.541 15048-15090 HWUI com.example.baiyunmap W Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without... 2025-07-14 19:12:21.541 15048-15090 HWUI com.example.baiyunmap W Failed to initialize 101010-2 format, error = EGL_SUCCESS 2025-07-14 19:12:21.569 15048-15048 nativeloader com.example.baiyunmap D Load /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so using class loader ns clns-9 (caller=/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk): ok 2025-07-14 19:12:21.573 15048-15048 System.err com.example.baiyunmap W java.lang.ClassNotFoundException: Didn't find class "com.baidu.platform.comjni.map.cloudcontrol.NACloudControl" on path: DexPathList[[zip file "/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk"],nativeLibraryDirectories=[/data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64, /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/base.apk!/lib/x86_64, /system/lib64, /system_ext/lib64]] 2025-07-14 19:12:21.573 15048-15048 System.err com.example.baiyunmap W at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259) 2025-07-14 19:12:21.574 15048-15048 System.err com.example.baiyunmap W at java.lang.ClassLoader.loadClass(ClassLoader.java:637) 2025-07-14 19:12:21.574 15048-15048 System.err com.example.baiyunmap W at java.lang.ClassLoader.loadClass(ClassLoader.java:573) 2025-07-14 19:12:21.575 15048-15048 System.err com.example.baiyunmap W at java.lang.Runtime.nativeLoad(Native Method) 2025-07-14 19:12:21.575 15048-15048 System.err com.example.baiyunmap W at java.lang.Runtime.loadLibrary0(Runtime.java:1088) 2025-07-14 19:12:21.576 15048-15048 System.err com.example.baiyunmap W at java.lang.Runtime.loadLibrary0(Runtime.java:1012) 2025-07-14 19:12:21.576 15048-15048 System.err com.example.baiyunmap W at java.lang.System.loadLibrary(System.java:1765) 2025-07-14 19:12:21.577 15048-15048 System.err com.example.baiyunmap W at com.baidu.mapsdkplatform.comapi.NativeLoader.a(NativeLoader.java:103) 2025-07-14 19:12:21.577 15048-15048 System.err com.example.baiyunmap W at com.baidu.mapsdkplatform.comapi.NativeLoader.loadLibrary(NativeLoader.java:84) 2025-07-14 19:12:21.577 15048-15048 System.err com.example.baiyunmap W at com.baidu.mapsdkplatform.comapi.map.i.<clinit>(EngineManager.java:272) 2025-07-14 19:12:21.578 15048-15048 System.err com.example.baiyunmap W at com.baidu.mapapi.map.MapView.a(MapView.java:456) 2025-07-14 19:12:21.579 15048-15048 System.err com.example.baiyunmap W at com.baidu.mapapi.map.MapView.<init>(MapView.java:179) 2025-07-14 19:12:21.579 15048-15048 System.err com.example.baiyunmap W at java.lang.reflect.Constructor.newInstance0(Native Method) 2025-07-14 19:12:21.579 15048-15048 System.err com.example.baiyunmap W at java.lang.reflect.Constructor.newInstance(Constructor.java:343) 2025-07-14 19:12:21.579 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.createView(LayoutInflater.java:743) 2025-07-14 19:12:21.579 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:895) 2025-07-14 19:12:21.580 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:850) 2025-07-14 19:12:21.581 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.rInflate(LayoutInflater.java:1012) 2025-07-14 19:12:21.581 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:973) 2025-07-14 19:12:21.582 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.inflate(LayoutInflater.java:571) 2025-07-14 19:12:21.582 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.inflate(LayoutInflater.java:462) 2025-07-14 19:12:21.582 15048-15048 System.err com.example.baiyunmap W at android.view.LayoutInflater.inflate(LayoutInflater.java:413) 2025-07-14 19:12:21.582 15048-15048 System.err com.example.baiyunmap W at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:577) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at android.app.Activity.setContentView(Activity.java:3892) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at com.example.baiyunmap.MainActivity.onCreate(MainActivity.java:78) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at android.app.Activity.performCreate(Activity.java:9155) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at android.app.Activity.performCreate(Activity.java:9133) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1521) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4262) 2025-07-14 19:12:21.583 15048-15048 System.err com.example.baiyunmap W at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4467) 2025-07-14 19:12:21.584 15048-15048 System.err com.example.baiyunmap W at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:222) 2025-07-14 19:12:21.584 15048-15048 System.err com.example.baiyunmap W at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:133) 2025-07-14 19:12:21.585 15048-15048 System.err com.example.baiyunmap W at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:103) 2025-07-14 19:12:21.585 15048-15048 System.err com.example.baiyunmap W at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80) 2025-07-14 19:12:21.586 15048-15048 System.err com.example.baiyunmap W at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2823) 2025-07-14 19:12:21.586 15048-15048 System.err com.example.baiyunmap W at android.os.Handler.dispatchMessage(Handler.java:110) 2025-07-14 19:12:21.586 15048-15048 System.err com.example.baiyunmap W at android.os.Looper.loopOnce(Looper.java:248) 2025-07-14 19:12:21.586 15048-15048 System.err com.example.baiyunmap W at android.os.Looper.loop(Looper.java:338) 2025-07-14 19:12:21.586 15048-15048 System.err com.example.baiyunmap W at android.app.ActivityThread.main(ActivityThread.java:9067) 2025-07-14 19:12:21.586 15048-15048 System.err com.example.baiyunmap W at java.lang.reflect.Method.invoke(Native Method) 2025-07-14 19:12:21.587 15048-15048 System.err com.example.baiyunmap W at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593) 2025-07-14 19:12:21.587 15048-15048 System.err com.example.baiyunmap W at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932) 2025-07-14 19:12:21.590 15048-15048 ample.baiyunmap com.example.baiyunmap W CheckJNI: method to register "nativeSetLayerTag" not in the given class. This is slow, consider changing your RegisterNatives calls. 2025-07-14 19:12:21.608 15048-15097 DEBUG com.example.baiyunmap D ThreadProc, CVSocketMan::SocketThreadProc start ... 2025-07-14 19:12:21.611 15048-15048 NetworkLogic com.example.baiyunmap D onNetWorkChanged-0, oldType = mobile 2025-07-14 19:12:21.613 15048-15048 PermissionCheck com.example.baiyunmap E permission check result is: -11 2025-07-14 19:12:21.613 15048-15098 NetworkLogic com.example.baiyunmap D NetworkDetect 2025-07-14 19:12:21.621 15048-15098 DEBUG com.example.baiyunmap D CNetworkDetectEngine::NetworkDetect Start 1 2025-07-14 19:12:21.664 15048-15048 libc com.example.baiyunmap A Fatal signal 11 (SIGSEGV), code 128 (SI_KERNEL), fault addr 0x0 in tid 15048 (ample.baiyunmap), pid 15048 (ample.baiyunmap) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A Cmdline: com.example.baiyunmap 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A pid: 15048, tid: 15048, name: ample.baiyunmap >>> com.example.baiyunmap <<< 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #00 pc 00000000004cab64 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (_baidu_framework::CBaseLayer::CBaseLayer()+148) (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #01 pc 000000000072d769 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #02 pc 0000000000741036 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #03 pc 000000000043abb0 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (_baidu_framework::CVComServer::ComCreateInstance(_baidu_vi::CVString const&, _baidu_vi::CVString const&, void**)+112) (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #04 pc 000000000052e8b1 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #05 pc 0000000000530502 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #06 pc 0000000000522990 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (_baidu_framework::IVMapbaseFactory::CreateInstance(_baidu_vi::CVString const&, void**)+128) (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #07 pc 000000000043abb0 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (_baidu_framework::CVComServer::ComCreateInstance(_baidu_vi::CVString const&, _baidu_vi::CVString const&, void**)+112) (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.746 15112-15112 DEBUG crash_dump64 A #08 pc 000000000048b694 /data/app/~~OljfxsydegH-sraOS4Y7Kg==/com.example.baiyunmap-QyH9YPNP5Ohzcm110rmefQ==/lib/x86_64/libBaiduMapSDK_map_for_navi_v7_6_5.so (BuildId: 72389d7e76ac01b9603c2b9a85daaaadbdc06757) 2025-07-14 19:12:22.750 15112-15112 DEBUG crash_dump64 A #143 pc 000000000000161c <anonymous:7784fd1de000> (com.example.baiyunmap.MainActivity.onCreate+0)
07-15
android 15中报错:// CRASH: com.android.mms (pid 32001) // Short Msg: java.lang.NullPointerException // Long Msg: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference // Build Label: SONIM/X53001/X530:15/X53.0-01-15.0-99.01.11/jenkins:userdebug/test-keys // Build Changelist: jenkins // Build Time: 1754634422000 // java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference // at com.android.mms.ui.AttachmentViewActivity.onDataLoadFinished(AttachmentViewActivity.java:281) // at com.android.mms.ui.BaseAttachmentViewActivity$GetDataAsyncTask.onPostExecute(BaseAttachmentViewActivity.java:222) // at com.android.mms.ui.BaseAttachmentViewActivity$GetDataAsyncTask.onPostExecute(BaseAttachmentViewActivity.java:190) // at android.os.AsyncTask.finish(AsyncTask.java:771) // at android.os.AsyncTask.-$$Nest$mfinish(Unknown Source:0) // at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788) // at android.os.Handler.dispatchMessage(Handler.java:107) // at android.os.Looper.loopOnce(Looper.java:232) // at android.os.Looper.loop(Looper.java:317) // at android.app.ActivityThread.main(ActivityThread.java:8794) // at java.lang.reflect.Method.invoke(Native Method) // at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) // at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:892) ,具体代码如下:AttachmentViewActivity.java:/* * Sonim Technologies, Inc. CONFIDENTIAL * Copyright (c) 2017 All Rights Reserved. * * The source code contained or described herein and all documents * related to the source code ("Material") is property of Sonim * Technologies, Inc. and its affiliates, may be covered by U.S. * or Foreign Patents or patents in process, and is protected by trade * secret or copyright law. No part of the Material may be used, * copied, reproduced, modified, published, uploaded, posted, * transmitted, distributed, or disclosed in any way without * prior written permission by Sonim Technologies, Inc. * * Unless required by applicable law or agreed to in writing, * the Materials are provided "AS IS," and WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, either express or implied. No license * under any patent, copyright, trade secret or other * intellectual property right is granted to or conferred upon you * by disclosure or delivery of the Material, either expressly, by * implication, inducement, estoppel or otherwise. Any license * under such intellectual property rights must be express and * approved by Sonim Technologies, Inc. in writing. * */ package com.android.mms.ui; import android.content.Context; import android.content.Intent; import android.os.Bundle; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.text.Html; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.Menu; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.android.mms.R; import com.android.mms.model.MediaModel; import com.android.mms.model.MediaWithTextModel; import com.android.mms.ui.multipleviews.BaseViewFragment; import com.android.mms.ui.multipleviews.VideoViewFragment; import com.google.android.mms.ContentType; import java.util.ArrayList; public class AttachmentViewActivity extends BaseAttachmentViewActivity { private static final String TAG = "AttachmentViewActivity"; static final int SAVE_SLIDER_ATTACHMENT_PERMISSION_REQUEST_CODE = 2017; private static final int DEFAULT_VISIBLE_LIST_COUNT = 3; private static final int DEFAULT_RECYCLER_VIEW_HEIGHT = 0; private static final int RECYCLER_VIEW_DEVICE_PORTION_ON_DEVICE_HEIGHT = 4; private static final boolean CONTAINER_FULLVIEW_MODE_VISIBLE = true; private static final boolean CONTAINER_FULLVIEW_MODE_INVISIBLE = false; private static final String VIEW_VCARD = "VIEW_VCARD_FROM_MMS"; private static final String KEY_PACKAGE_NAME = "calling_package_name"; private static final String PACKAGE_NAME = "com.android.mms"; private static final String EXTRA_SINGLE_ITEM = "SingleItemOnly"; private LinearLayout mDotsLayout; private TextView[] mDotsTextview; private SlidesAdapter mSlidesAdapter; private RecyclerView mSlidesRecyclerView; /** * Default recycler view height will be zero. Based on Device height it will be calculate the * recyclerview height dynamically. */ private int mRecyclerViewHeight = 0; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); if (MessageUtils.checkPermissionsIfNeeded(this)) { return; } setContentView(R.layout.attachment_view_activity); new GetDataAsyncTask(this).execute(); } private void initView() { mSelectedAttachmentContainer = (LinearLayout) findViewById(R.id.frameLayout_container); mDotsLayout = (LinearLayout) findViewById(R.id.dots_layout); mSlidesRecyclerView = (RecyclerView) findViewById(R.id.slides_recycler_view); calculateRecyclerViewHeight(); resetRecyclerViewHeight(mRecyclerViewHeight); RecyclerView.LayoutManager layoutManager = new LinearLayoutManagerWrapper(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false); mSlidesRecyclerView.setLayoutManager(layoutManager); mSlidesRecyclerView.setItemAnimator(new DefaultItemAnimator()); mSlidesAdapter = new SlidesAdapter(mMediaWithTextModelList, this); mSlidesRecyclerView.setAdapter(mSlidesAdapter); // << [DNL-7826] 20210422 JunYiLin >> mSlidesAdapter.setOnDataSetChangedListener(mDataSetChangedListener); // << [DNL-7080] 20210330 JunYiLin << mSlidesRecyclerView.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { updateNavBar(getResources().getStringArray(R.array.setNavBar_highlight_attachments_view_in_slideshow)); } } }); // [DNL-7080] >> } /** * prepare display dots layout */ private void displayDotsLayout() { if (mDotsTextview == null) { if (mMediaWithTextModelList.size() % DEFAULT_VISIBLE_LIST_COUNT == 0) { mDotsTextview = new TextView[mMediaWithTextModelList.size() / DEFAULT_VISIBLE_LIST_COUNT]; } else { mDotsTextview = new TextView[ (mMediaWithTextModelList.size() / DEFAULT_VISIBLE_LIST_COUNT) + 1]; } for (int i = 0; i < mDotsTextview.length; i++) { TextView dotTextView = new TextView(this); mDotsTextview[i] = dotTextView; mDotsTextview[i].setText(Html.fromHtml(getResources(). getString(R.string.dots_text_design))); mDotsTextview[i].setTextSize(getResources().getDimension(R.dimen.dot_text_design_size)); mDotsTextview[i].setTextColor(getResources().getColor(R.color.dot_inactive)); Log.v(TAG, "mDotsTextview text: "+mDotsTextview[i].getText()); mDotsLayout.addView(mDotsTextview[i]); } changeDotsColor(0); } Log.d(TAG,"fxj 147"); // Message UABT-1662 Haifei.Liao remove the dot layout BEGIN mDotsLayout.setVisibility(View.GONE); // Message UABT-1662 Haifei.Liao remove the dot layout END } @Override public void onRequestPermissionsResult(final int requestCode, final String permissions[], final int[] grantResults) { if (requestCode == SAVE_SLIDER_ATTACHMENT_PERMISSION_REQUEST_CODE) { if (MessageUtils.hasStoragePermission()) { //TODO: Check for permission enabled or not } else { Toast.makeText(this, R.string.no_permission_save_attachment_to_storage, Toast.LENGTH_SHORT).show(); } } } /** * change dots color whenever focus going to change left to right or right to left * @param currentPage */ private void changeDotsColor(int currentPage) { if (mDotsTextview == null) { return; } int focus = currentPage / DEFAULT_VISIBLE_LIST_COUNT; if (focus > 0) { mDotsTextview[focus - 1].setTextColor(getResources().getColor(R.color.dot_inactive)); } if (focus < mDotsTextview.length - 1) { mDotsTextview[focus + 1].setTextColor(getResources().getColor(R.color.dot_inactive)); } mDotsTextview[focus].setTextColor(getResources().getColor(R.color.dot_active)); } @Override public void onBackPressed() { /** * When click on back button check for is it in full view mode or not. If it is in full view mode * adjust to normal mode without close application. If it is in normal mode close this activity */ if (mSlidesRecyclerView.getLayoutParams() != null && mSlidesRecyclerView.getLayoutParams().height == DEFAULT_RECYCLER_VIEW_HEIGHT) { if (isSingleSlideMode()) { finish(); } else { resetRecyclerViewHeight(CONTAINER_FULLVIEW_MODE_INVISIBLE); } } else { super.onBackPressed(); } } @Override public void onItemClick(int position) { /** * If seleted mediaModel is Vcard or Vcal open there respective actions * else adjust current view as full view. */ MediaModel mediaModel = mMediaWithTextModelList.get(position).getMediaModel(); if (mediaModel != null) { if (mediaModel.isVcard()) { Intent intent = new Intent(Intent.ACTION_VIEW); // we need open the saved part. intent.setDataAndType(mediaModel.getUri(), ContentType.TEXT_VCARD.toLowerCase()); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // distinguish view vcard from mms or contacts. intent.putExtra(VIEW_VCARD, true); Intent openInChooser = Intent.createChooser(intent, getString(R.string.action_header)); startActivity(openInChooser); } else if (mediaModel.isVcal()) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(EXTRA_SINGLE_ITEM, true); // So we don't see "surrounding" images in Gallery intent.putExtra(KEY_PACKAGE_NAME, PACKAGE_NAME); String contentType; contentType = mediaModel.getContentType(); intent.setDataAndType(mediaModel.getUri(), contentType); startActivity(intent); } else { resetRecyclerViewHeight(CONTAINER_FULLVIEW_MODE_VISIBLE); } } } @Override public void onLoadContainerView(int position) { super.onLoadContainerView(position); updateCurrentPosition(position); Log.v(TAG, "Attachment no fragment loaded till now onLoadContainerView"); changeDotsColor(position); } /** * Update mediaWithTextModel on mediaModel and text available * @param mediaModel * @param message */ @Override public void onDataUpdate(final MediaModel mediaModel, final String message) { runOnUiThread(new Runnable() { @Override public void run() { if(mActualNumberOfSlides == mNumberOfSlidesLoaded) { onDataInsert(mediaModel, message); } else { MediaWithTextModel mediaWithTextModel = mMediaWithTextModelList.get(mNumberOfSlidesLoaded); mediaWithTextModel.setMediaModel(mediaModel); mediaWithTextModel.setMessageText(message); mMediaWithTextModelList.set(mNumberOfSlidesLoaded, mediaWithTextModel); mSlidesAdapter.notifyItemChanged(mNumberOfSlidesLoaded); mNumberOfSlidesLoaded = mNumberOfSlidesLoaded + 1; } } }); } /** * Method for once data loads done this method will trigger. Handle on Main Thread */ @Override public void onDataLoadFinished() { Log.v(TAG, "Attachment onDataLoadFinished media list size is " + mMediaWithTextModelList.size()); removeEmptySlideObjects(); displayDotsLayout(); } @Override public void setInitialSlideSize(int size) { super.setInitialSlideSize(size); mMediaWithTextModelList = new ArrayList<MediaWithTextModel>(size); for(int i = 0; i < size; i ++) { mMediaWithTextModelList.add(new MediaWithTextModel(null,null)); } runOnUiThread(new Runnable() { @Override public void run() { initView(); } }); } @Override public void onDataInsert(final MediaModel mediaModel, final String message) { runOnUiThread(new Runnable() { @Override public void run() { Log.v(TAG, "Attachment onDataInsert"); MediaWithTextModel mediaWithTextModel = new MediaWithTextModel(message, mediaModel); mMediaWithTextModelList.add(mediaWithTextModel); mSlidesAdapter.notifyItemInserted(mMediaWithTextModelList.size() - 1); } }); } @Override public void onDataRemove(final int position) { mSlidesAdapter.notifyItemRemoved(position); } /** * sometimes slidemodel will return empty mediamodel list */ private void removeEmptySlideObjects() { //check for number of slides reached or not. If not reached then remove dummy viewholder from recyclerview runOnUiThread(new Runnable() { @Override public void run() { if(mActualNumberOfSlides > mNumberOfSlidesLoaded) { mMediaWithTextModelList.remove(mNumberOfSlidesLoaded); mNumberOfSlidesLoaded = mNumberOfSlidesLoaded + 1; onDataRemove(mMediaWithTextModelList.size()); removeEmptySlideObjects(); } } }); } public static class LinearLayoutManagerWrapper extends LinearLayoutManager { public LinearLayoutManagerWrapper(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } @Override public boolean supportsPredictiveItemAnimations() { return false; } } /** * Calculate recycler view height using the device height * eg: if device height is 400 then recycleViewHeight = 400/4 * (Here 4 is portion of value). finally recyclerViewHeight = 100 */ private void calculateRecyclerViewHeight() { DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int height = displayMetrics.heightPixels; mRecyclerViewHeight = (int) (height / RECYCLER_VIEW_DEVICE_PORTION_ON_DEVICE_HEIGHT ); } public void resetRecyclerViewHeight(boolean isViewEntered) { Log.d(TAG, "base resetRecyclerViewHeight isEnter = " + isViewEntered); BaseViewFragment baseFragment = (BaseViewFragment) getFragmentManager() .findFragmentById(R.id.selected_attachment_container); if (isViewEntered) { resetRecyclerViewHeight(DEFAULT_RECYCLER_VIEW_HEIGHT); mDotsLayout.setVisibility(View.GONE); if (baseFragment != null) { baseFragment.onPlayMedia(); } } else { resetRecyclerViewHeight(mRecyclerViewHeight); mDotsLayout.setVisibility(View.VISIBLE); if (baseFragment != null) { baseFragment.onStopMedia(); } } mSlidesRecyclerView.requestLayout(); } private void resetRecyclerViewHeight(int height) { mSlidesRecyclerView.getLayoutParams().height = height; mSlidesRecyclerView.requestLayout(); } /** * When click play all menu item readjust view from full view to normal */ @Override public void onPlayAllMenuItemSelect() { if (mSlidesRecyclerView.getLayoutParams().height == DEFAULT_RECYCLER_VIEW_HEIGHT) { resetRecyclerViewHeight(CONTAINER_FULLVIEW_MODE_INVISIBLE); } } @Override public void playMediaInFullView() { resetRecyclerViewHeight(CONTAINER_FULLVIEW_MODE_VISIBLE); } @Override public boolean dispatchKeyEvent(KeyEvent event) { BaseViewFragment baseFragment = (BaseViewFragment) getFragmentManager() .findFragmentById(R.id.selected_attachment_container); if(baseFragment instanceof VideoViewFragment) { VideoViewFragment videoViewFragment = (VideoViewFragment) baseFragment; videoViewFragment.onKeyEvent(); } return super.dispatchKeyEvent(event); } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); invalidateOptionsMenu(); return true; } // [DNL-7080] 20210330 JunYiLin << @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_MENU: saveAllAttachment(); break; case KeyEvent.KEYCODE_MULTIFUNC_CENTER: playAllSlideShow(); break; case KeyEvent.KEYCODE_MULTIFUNC_RIGHT: saveAttachment(); break; } return super.onKeyUp(keyCode, event); } @Override protected void onResume() { super.onResume(); updateNavBar(this.getResources().getStringArray(R.array.setNavBar_highlight_attachments_view_in_slideshow)); } // [DNL-7080] >> },BaseAttachmentViewActivity.java:/* * Sonim Technologies, Inc. CONFIDENTIAL * Copyright (c) 2017 All Rights Reserved. * * The source code contained or described herein and all documents * related to the source code ("Material") is property of Sonim * Technologies, Inc. and its affiliates, may be covered by U.S. * or Foreign Patents or patents in process, and is protected by trade * secret or copyright law. No part of the Material may be used, * copied, reproduced, modified, published, uploaded, posted, * transmitted, distributed, or disclosed in any way without * prior written permission by Sonim Technologies, Inc. * * Unless required by applicable law or agreed to in writing, * the Materials are provided "AS IS," and WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, either express or implied. No license * under any patent, copyright, trade secret or other * intellectual property right is granted to or conferred upon you * by disclosure or delivery of the Material, either expressly, by * implication, inducement, estoppel or otherwise. Any license * under such intellectual property rights must be express and * approved by Sonim Technologies, Inc. in writing. * */ package com.android.mms.ui; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.provider.Telephony; import android.util.Log; import android.os.Environment; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; // << [DNL-795] 20210113 JunYiLin >> import android.view.KeyEvent; import android.webkit.MimeTypeMap; import android.widget.LinearLayout; import android.widget.Toast; import com.android.mms.MmsApp; import com.android.mms.R; import com.android.mms.drm.DrmUtils; import com.android.mms.model.MediaWithTextModel; import com.android.mms.model.MediaModel; import com.android.mms.model.SlideModel; import com.android.mms.model.SlideshowModel; import com.android.mms.model.SmilHelper; import com.android.mms.transaction.MessagingNotification; import com.android.mms.ui.multipleviews.AudioViewFragment; import com.android.mms.ui.multipleviews.BaseViewFragment; import com.android.mms.ui.multipleviews.IAttachmentSelection; import com.android.mms.ui.multipleviews.ImageViewFragment; import com.android.mms.ui.multipleviews.VcardVcalViewFragment; import com.android.mms.ui.multipleviews.VideoViewFragment; import com.android.mms.util.Utils; import com.google.android.mms.ContentType; import com.google.android.mms.MmsException; import com.google.android.mms.util.SqliteWrapper; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.List; public abstract class BaseAttachmentViewActivity extends Activity implements IAttachmentSelection { private static final String TAG = BaseAttachmentViewActivity.class.getSimpleName(); private static final int SINGLE_SLIDE_MODEL_SIZE = 1; private static final String NO_SPACE_ERROR = "ENOSPC"; private static boolean mNoSpace = false; public static final int MENU_PLAY = 1; public static final int MENU_PAUSE = 2; public static final int MENU_RESUME = 3; public static final int MENU_PLAY_ALL = 4; public static final int MENU_STOP = 5; public static final int MENU_SAVE_ATTACHMENT = 6; public static final int MENU_SAVE_ALL_ATTACHMENTS = 7; public LinearLayout mSelectedAttachmentContainer; public List<MediaWithTextModel> mMediaWithTextModelList; private boolean mSingleSlideMode = false; private boolean mPlayAllMode = false; private boolean mEditSlideShowMode = false; private int mCurrentMediaPosition = 0; public int mActualNumberOfSlides, mNumberOfSlidesLoaded; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } private void markAsReadIfNeed() { Intent intent = getIntent(); boolean unread = intent.getBooleanExtra("unread", false); Uri uri = intent.getData(); if (unread) { ContentValues values = new ContentValues(2); values.put(Telephony.Mms.SEEN, MessageUtils.MESSAGE_SEEN); values.put(Telephony.Mms.READ, MessageUtils.MESSAGE_READ); SqliteWrapper.update(this, getContentResolver(), uri, values, null, null); MessagingNotification.blockingUpdateNewMessageIndicator( this, MessagingNotification.THREAD_NONE, false); } } @Override public void onLoadContainerView(int position) { MediaWithTextModel mediaWithTextModel = mMediaWithTextModelList.get(position); overrideContainer(mediaWithTextModel); } @Override public void onLoadContainerView(MediaWithTextModel customMediaModel) { overrideContainer(customMediaModel); } // When switching between the fragments update mCurrentMediaPosition // with selected media position public void updateCurrentPosition(int currentMediaPosition) { mCurrentMediaPosition = currentMediaPosition; } private void overrideContainer(MediaWithTextModel mediaWithTextModel) { if (!isFinishing() && !isDestroyed()) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); MediaModel mediaModel = mediaWithTextModel.getMediaModel(); switch (mediaModel.getTag()) { case SmilHelper.ELEMENT_TAG_TEXT: fragmentTransaction.replace(R.id.selected_attachment_container, ImageViewFragment.newInstance(mediaWithTextModel), null); break; case SmilHelper.ELEMENT_TAG_IMAGE: fragmentTransaction.replace(R.id.selected_attachment_container, ImageViewFragment.newInstance(mediaWithTextModel), null); break; case SmilHelper.ELEMENT_TAG_AUDIO: fragmentTransaction.replace(R.id.selected_attachment_container, AudioViewFragment.newInstance(mediaWithTextModel), null); break; case SmilHelper.ELEMENT_TAG_VIDEO: fragmentTransaction.replace(R.id.selected_attachment_container, VideoViewFragment.newInstance(mediaWithTextModel), null); break; case SmilHelper.ELEMENT_TAG_REF: if (mediaModel.getContentType().toLowerCase().equals(ContentType.TEXT_VCARD. toLowerCase()) || mediaModel.getContentType().toLowerCase(). equals(ContentType.TEXT_VCALENDAR.toLowerCase())) { fragmentTransaction.replace(R.id.selected_attachment_container, VcardVcalViewFragment.newInstance(mediaWithTextModel), null); } break; default: Log.v(TAG,"default attachment type case"); } fragmentTransaction.commit(); } } public boolean isSingleSlideMode() { return mSingleSlideMode; } public void setSingleSlideMode(boolean mSingleSlideMode) { this.mSingleSlideMode = mSingleSlideMode; } static class GetDataAsyncTask extends AsyncTask<Void, Void, Void> { private WeakReference<BaseAttachmentViewActivity> attachmentViewActivityWeakReference; public GetDataAsyncTask(BaseAttachmentViewActivity baseAttachmentViewActivity) { attachmentViewActivityWeakReference = new WeakReference(baseAttachmentViewActivity); } @Override protected Void doInBackground(Void... voids) { Log.v(TAG, "GetDataAsyncTask doInBackground"); BaseAttachmentViewActivity activity = attachmentViewActivityWeakReference.get(); if (activity != null) { try { Intent intent = activity.getIntent(); Uri uri = intent.getData(); SlideshowModel.createFromMessageUri(activity, uri); } catch (MmsException e) { Log.e(TAG, "Cannot present the slide show.", e); } activity.markAsReadIfNeed(); } else { Log.v(TAG, "GetDataAsyncTask Activity is null "); } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); BaseAttachmentViewActivity activity = attachmentViewActivityWeakReference.get(); if (activity != null) { activity.onDataLoadFinished(); } } } /** * Update mediaWithTextModelList on slideModel available * @param slideModel * @param currentSlide */ @Override public void onDataUpdate(SlideModel slideModel, int currentSlide) { String message = slideModel.getText() != null ? slideModel.getText().getText() : null; if (slideModel.size() == SINGLE_SLIDE_MODEL_SIZE && slideModel.get(0).isText()) { if (mActualNumberOfSlides > mNumberOfSlidesLoaded) { onDataUpdate(slideModel.get(0), message); } else { onDataInsert(slideModel.get(0), message); } } else { for (int j = 0; j < slideModel.size(); j++) { MediaModel mediaModel = slideModel.get(j); Log.d(TAG, "Attachment prepareSlideData MediaModel contentType = " + mediaModel.getContentType()); if (mediaModel != null && !mediaModel.isText()) { Log.d(TAG, "Attachment prepareSlideData adding MediaWithTextModel "); if (mActualNumberOfSlides > mNumberOfSlidesLoaded) { onDataUpdate(mediaModel, message); } else { onDataInsert(mediaModel, message); } } } } } @Override public void setInitialSlideSize(int size) { Log.v(TAG, "Attachment setSlideShowSize size " + size); mActualNumberOfSlides = size; } public IAttachmentSelection getIAttachmentSelection() { return this; } public boolean isPlayAllMode() { return mPlayAllMode; } public void setPlayAllMode(boolean mPlayAllMode) { this.mPlayAllMode = mPlayAllMode; } @Override public void onPlayNextSlide() { } //[DNL-795] 20210220 JunYiLin << /* mark original @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); BaseViewFragment baseFragment = (BaseViewFragment) getFragmentManager(). findFragmentById(R.id.selected_attachment_container); if(baseFragment != null) { baseFragment.onMenuUpdate(menu); } if(mMediaWithTextModelList != null && mMediaWithTextModelList.size() > 1) { menu.add(0, MENU_PLAY_ALL, 0,R.string.play_all); } menu.add(0, MENU_SAVE_ATTACHMENT, 0,R.string.save_attachment); if(mMediaWithTextModelList != null && mMediaWithTextModelList.size() > 1) { menu.add(0, MENU_SAVE_ALL_ATTACHMENTS, 0, R.string.save_all_attachments); } return true; } */ // [DNL-795] >> @Override public boolean onOptionsItemSelected(MenuItem item) { BaseViewFragment baseFragment = null; switch (item.getItemId()) { case MENU_PLAY_ALL: onPlayAllMenuItemSelect(); Intent intent = new Intent(this, AttachmentPlayAllActivity.class); intent.setData(getIntent().getData()); startActivity(intent); break; case MENU_SAVE_ATTACHMENT: saveAttachment(); break; case MENU_SAVE_ALL_ATTACHMENTS: saveAllAttachment(); break; case MENU_PLAY: playMediaInFullView(); baseFragment = (BaseViewFragment) getFragmentManager() .findFragmentById(R.id.selected_attachment_container); baseFragment.onPlayMedia(); break; case MENU_PAUSE: baseFragment = (BaseViewFragment) getFragmentManager() .findFragmentById(R.id.selected_attachment_container); baseFragment.onPauseMedia(); break; case MENU_RESUME: baseFragment = (BaseViewFragment) getFragmentManager() .findFragmentById(R.id.selected_attachment_container); baseFragment.onResumeMedia(); break; default: return super.onOptionsItemSelected(item); } return true; } // [DNL-7080] 20210330 JunYiLin << public void saveAllAttachment() { /* mark original private void saveAllAttachment(){ */ // [DNL-7080] int resId; boolean isCopied = copyMedia(); if(isCopied){ resId = R.string.save_all_attachment_success; }else{ resId = mNoSpace ? R.string.space_low_message : R.string.save_attachment_fail; } Toast.makeText(BaseAttachmentViewActivity.this, resId, Toast.LENGTH_SHORT).show(); } // [DNL-795] 20210113 JunYiLin << public void saveAttachment() { /* mark original private void saveAttachment(){ */ // [DNL-795] >> int resId; boolean isCopied = copyPart(mMediaWithTextModelList.get(mCurrentMediaPosition)); if(isCopied){ resId = R.string.save_attachment_success; }else{ resId = mNoSpace ? R.string.space_low_message : R.string.save_attachment_fail; } Toast.makeText(BaseAttachmentViewActivity.this, resId, Toast.LENGTH_SHORT).show(); } /** * Copies media from an Mms to the "download" directory on the SD card. If any of the parts * are audio types, drm'd or not, they're copied to the "Ringtones" directory. */ private boolean copyMedia() { boolean result = true; if (mMediaWithTextModelList == null) { return false; } for(int i = 0; i < mMediaWithTextModelList.size(); i++) { // all parts have to be successful for a valid result. result &= copyPart(mMediaWithTextModelList.get(i)); } return result; } private boolean copyPart(MediaWithTextModel customMediaModel) { // [DNL-8241] 20210423 JunYiLin << if (MessageUtils.checkIsPhoneMemoryFull(BaseAttachmentViewActivity.this)) { mNoSpace = true; return false; } // [DNL-8241] >> mNoSpace = false; MediaModel mediaModel = customMediaModel.getMediaModel(); Uri uri = mediaModel.getUri(); String type = new String(mediaModel.getContentType()); boolean isDrm = DrmUtils.isDrmType(type); if (isDrm) { type = MmsApp.getApplication().getDrmManagerClient() .getOriginalMimeType(uri); } if (!ContentType.isImageType(type) && !ContentType.isVideoType(type) && !ContentType.isAudioType(type) && !(ContentType.TEXT_VCARD.toLowerCase().equals(type.toLowerCase())) && !(ContentType.TEXT_VCALENDAR.toLowerCase().equals(type.toLowerCase())) && !(ContentType.AUDIO_OGG.toLowerCase().equals(type.toLowerCase()))) { return true; // we only save pictures, videos, and sounds. Skip the text parts, // the app (smil) parts, and other type that we can't handle. // Return true to pretend that we successfully saved the part so // the whole save process will be counted a success. } InputStream input = null; FileOutputStream fout = null; try { input = getContentResolver().openInputStream(uri); if (input instanceof FileInputStream) { FileInputStream fin = (FileInputStream) input; String fileName = mediaModel.getSrc(); File originalFile = new File(fileName); fileName = originalFile.getName(); // Strip the full path of where the "part" is // stored down to just the leaf filename. // For every media the default saving directory should be 'Download'. String dir = Utils.getStoragePath(this) + "/" + Environment.DIRECTORY_DOWNLOADS + "/"; String extension; int index; if ((index = fileName.lastIndexOf('.')) == -1) { extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(type); } else { extension = fileName.substring(index + 1, fileName.length()); fileName = fileName.substring(0, index); } if (isDrm) { extension += DrmUtils.getConvertExtension(type); } // Remove leading periods. The gallery ignores files starting with a period. if (!TextUtils.isEmpty(fileName)) { fileName = fileName.trim(); fileName = fileName.replaceAll("^\\.", ""); } File file = getUniqueDestination(dir + fileName, extension); // make sure the path is valid and directories created for this file. File parentFile = file.getParentFile(); if (!parentFile.exists() && !parentFile.mkdirs()) { Log.e(TAG, "[MMS] copyPart: mkdirs for " + parentFile.getPath() + " failed!"); return false; } fout = new FileOutputStream(file); byte[] buffer = new byte[8000]; int size = 0; while ((size=fin.read(buffer)) != -1) { fout.write(buffer, 0, size); } // Notify other applications listening to scanner events // that a media file has been added to the sd card sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); } } catch (IOException e) { // Ignore Log.e(TAG, "IOException caught while opening or reading stream", e); if(e.getMessage().contains(NO_SPACE_ERROR)){ mNoSpace = true; } return false; } finally { if (null != input) { try { input.close(); } catch (IOException e) { // Ignore Log.e(TAG, "IOException caught while closing stream", e); return false; } } if (null != fout) { try { fout.close(); } catch (IOException e) { // Ignore Log.e(TAG, "IOException caught while closing stream", e); return false; } } } return true; } private File getUniqueDestination(String base, String extension) { File file = new File(base + "." + extension); for (int i = 2; file.exists(); i++) { file = new File(base + "_" + i + "." + extension); } return file; } @Override public void onPlayAllMenuItemSelect() { } @Override public void playMediaInFullView() {} public boolean isEditSlideShowMode() { return mEditSlideShowMode; } public void setEditSlideShowMode(boolean editSlideShowMode) { this.mEditSlideShowMode = editSlideShowMode; } // << [DNL-7080] 20210330 JunYiLin << public void updateNavBar(String[] str) { if (str.length > 0) { MessageUtils.setNavigationBarName(this, str); } } public void playAllSlideShow() { Intent intent = new Intent(this, AttachmentPlayAllActivity.class); intent.setData(getIntent().getData()); startActivity(intent); } // [DNL-7080] >> // [DNL-7826] 20210422 JunYiLin << public final SlidesAdapter.OnDataSetChangedListener mDataSetChangedListener = new SlidesAdapter.OnDataSetChangedListener() { @Override public void onHighLightItem(int position) { playAudioMedia(position); } }; public void playAudioMedia(int position) { MediaModel mediaModel = mMediaWithTextModelList.get(position).getMediaModel(); if (mediaModel != null && mediaModel.isAudio()) { BaseViewFragment baseFragment = (BaseViewFragment) getFragmentManager().findFragmentById(R.id.selected_attachment_container); if (baseFragment != null) { baseFragment.onPlayMedia(); } } } // [DNL-7826] >> }以上报错,怎么在上述代码上具体修改
最新发布
08-12
MainActivity.java: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; import android.view.MotionEvent; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URLEncoder; import org.json.JSONObject; import android.content.Intent; import android.widget.Button; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; 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 int DIRECTION_UP = 0; private static final int DIRECTION_DOWN = 1; private static final int DIRECTION_LEFT = 2; private static final int DIRECTION_RIGHT = 3; private static final int DIRECTION_ZOOM_IN = 8; // 物理放大 private static final int DIRECTION_ZOOM_OUT = 9; // 物理缩小 private static final int DIRECTION_FOCUS_NEAR = 10; // 调整近焦距 private static final int DIRECTION_FOCUS_FAR = 11; // 调整远焦距 private static final int SPEED_MEDIUM = 1; // 速度适中 private static final String PTZ_START_URL = "https://open.ys7.com/api/lapp/device/ptz/start"; private static final String PTZ_STOP_URL = "https://open.ys7.com/api/lapp/device/ptz/stop"; private boolean mIsSupportPTZ = false; // 设备是否支持云台 private ExecutorService mExecutorService; // 线程池 private volatile boolean isPTZActive = false; // 标记是否有云台操作正在进行 private volatile int activeDirection = -1; // 当前活动的方向 private boolean mHasShownConnectionSuccess = false; // 接收的参数键 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; private RelativeLayout mRaTitle; // 从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; // 新增:云台控制重试机制相关变量 private static final int MAX_PTZ_RETRIES = 2; private Map<Integer, Integer> ptzRetryCountMap = new ConcurrentHashMap<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activitymain); mHandler = new Handler(this); mRaTitle = findViewById(R.id.ra_title); // 1. 从Intent中获取参数 extractParametersFromIntent(); // 2. 初始化UI initUI(); initOrientationSensitiveViews(); View fanHui = findViewById(R.id.back); fanHui.setOnClickListener(v -> finish()); Button huifangBtn = findViewById(R.id.huifang); huifangBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 创建Intent跳转到FanHui活动 Intent intent = new Intent(MainActivity.this, FanHui.class); // 传递必要参数(可选) intent.putExtra("deviceSerial", mDeviceSerial); intent.putExtra("cameraNo", mCameraNo); intent.putExtra("accessToken", mAccessToken); intent.putExtra("appkey", mAppKey); intent.putExtra("verifyCode", mVerifyCode); startActivity(intent); } }); // 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(); // 创建线程池 mExecutorService = Executors.newFixedThreadPool(2); // 4. 初始化云台控制按钮 initPTZButtons(); } private void initPTZButtons() { // 竖屏布局的按钮 setupPTZButton(R.id.ptz_left_btn, DIRECTION_LEFT); setupPTZButton(R.id.ptz_right_btn, DIRECTION_RIGHT); setupPTZButton(R.id.ptz_top_btn, DIRECTION_UP); setupPTZButton(R.id.ptz_bottom_btn, DIRECTION_DOWN); setupPTZButton(R.id.zoom_add, DIRECTION_ZOOM_IN); // 物理放大 setupPTZButton(R.id.zoom_reduce, DIRECTION_ZOOM_OUT); // 物理缩小 setupPTZButton(R.id.focus_add, DIRECTION_FOCUS_NEAR); // 近焦距 setupPTZButton(R.id.foucus_reduce, DIRECTION_FOCUS_FAR); // 远焦距 setupUnsupportedButton(R.id.guangquan_add, "光圈+"); setupUnsupportedButton(R.id.guangquan_reduce, "光圈-"); // 横屏布局的按钮 setupPTZButton(R.id.ptz_left_btn2, DIRECTION_LEFT); setupPTZButton(R.id.ptz_right_btn2, DIRECTION_RIGHT); setupPTZButton(R.id.ptz_top_btn2, DIRECTION_UP); setupPTZButton(R.id.ptz_bottom_btn2, DIRECTION_DOWN); setupPTZButton(R.id.zoom_add2, DIRECTION_ZOOM_IN); setupPTZButton(R.id.zoom_reduce2, DIRECTION_ZOOM_OUT); setupPTZButton(R.id.focus_add2, DIRECTION_FOCUS_NEAR); // 近焦距 setupPTZButton(R.id.foucus_reduce2, DIRECTION_FOCUS_FAR); // 远焦距 } private void setupUnsupportedButton(int buttonId, String buttonName) { ImageButton button = findViewById(buttonId); if (button != null) { button.setOnClickListener(v -> { Toast.makeText(MainActivity.this, "SDK不支持此功能", Toast.LENGTH_SHORT).show(); }); } } private void setupPTZButton(int buttonId, final int direction) { ImageButton button = findViewById(buttonId); if (button != null) { button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 按下时开始云台动作 controlPTZ(direction, SPEED_MEDIUM, true); return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // 如果这是当前活动的方向,则停止 if (isPTZActive && activeDirection == direction) { controlPTZ(direction, SPEED_MEDIUM, false); } return true; } return false; } }); } } // 重构后的云台控制方法 private void controlPTZ(final int direction, final int speed, final boolean isStart) { // 检查是否允许开始新操作 if (isStart) { if (isPTZActive) { // 如果有活动操作,先停止当前操作 controlPTZ(activeDirection, speed, false); // 延迟100ms后再开始新操作 new Handler().postDelayed(() -> controlPTZ(direction, speed, true), 100); return; } } // 重试次数检查 Integer retryCount = ptzRetryCountMap.getOrDefault(direction, 0); if (retryCount >= MAX_PTZ_RETRIES) { Log.w(TAG, "达到最大重试次数,放弃操作 direction=" + direction); ptzRetryCountMap.remove(direction); resetPTZState(); return; } mExecutorService.execute(() -> { try { String urlStr = isStart ? PTZ_START_URL : PTZ_STOP_URL; URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); // 构建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); if (isStart) { postData.append("&direction=").append(direction); postData.append("&speed=").append(speed); } else { // 停止时带上方向参数(建议) postData.append("&direction=").append(direction); } // 发送请求 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"); String msg = json.optString("msg", "未知错误"); if ("200".equals(code)) { Log.d(TAG, "PTZ控制成功: " + (isStart ? "开始" : "停止") + " | 方向: " + direction); // 更新状态 if (isStart) { isPTZActive = true; activeDirection = direction; } else { isPTZActive = false; activeDirection = -1; } // 成功时重置重试计数器 ptzRetryCountMap.remove(direction); } else { // 增加重试计数 ptzRetryCountMap.put(direction, retryCount + 1); if (isStart) { Log.w(TAG, "PTZ控制失败,尝试重试 direction=" + direction + ", retry=" + (retryCount + 1)); controlPTZ(direction, speed, true); } else { // 停止命令失败需要特殊处理 handleStopFailure(direction); } handlePTZError(code, msg, isStart); } } else { // 增加重试计数 ptzRetryCountMap.put(direction, retryCount + 1); if (isStart) { Log.w(TAG, "PTZ控制失败,尝试重试 direction=" + direction + ", retry=" + (retryCount + 1)); controlPTZ(direction, speed, true); } else { // 停止命令失败需要特殊处理 handleStopFailure(direction); } Log.e(TAG, "HTTP错误: " + responseCode); runOnUiThread(() -> Toast.makeText(MainActivity.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } conn.disconnect(); } catch (Exception e) { // 增加重试计数 ptzRetryCountMap.put(direction, retryCount + 1); if (isStart && retryCount < MAX_PTZ_RETRIES) { Log.w(TAG, "PTZ控制异常,尝试重试", e); controlPTZ(direction, speed, true); } else { handleStopFailure(direction); } Log.e(TAG, "PTZ控制异常", e); runOnUiThread(() -> Toast.makeText(MainActivity.this, "云台控制出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } }); } // 新增:处理停止失败的情况 private void handleStopFailure(int direction) { runOnUiThread(() -> { // 紧急停止:发送不带方向参数的停止命令 emergencyStopPTZ(); Toast.makeText(MainActivity.this, "云台停止失败,已执行紧急停止", Toast.LENGTH_SHORT).show(); }); // 重置状态 resetPTZState(); } // 新增:紧急停止方法(不带方向参数) private void emergencyStopPTZ() { mExecutorService.execute(() -> { try { URL url = new URL(PTZ_STOP_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); // 构建不带方向参数的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); // 发送请求 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)) { Log.d(TAG, "紧急停止成功"); } else { Log.w(TAG, "紧急停止失败: " + json.optString("msg", "未知错误")); } } else { Log.e(TAG, "紧急停止HTTP错误: " + responseCode); } conn.disconnect(); } catch (Exception e) { Log.e(TAG, "紧急停止异常", e); } }); } // 新增:重置云台状态 private void resetPTZState() { isPTZActive = false; activeDirection = -1; ptzRetryCountMap.clear(); } private void handlePTZError(String code, String msg, boolean isStart) { String errorMessage = msg; // 根据文档映射错误信息 switch (code) { case "10001": errorMessage = "参数错误"; break; case "10002": errorMessage = "accessToken异常或过期"; break; case "20002": errorMessage = "设备不存在"; break; case "20007": errorMessage = "设备不在线"; break; case "60000": errorMessage = "设备不支持云台控制"; mIsSupportPTZ = false; // 更新设备支持状态 break; case "60001": errorMessage = "用户无云台控制权限"; break; case "60020": errorMessage = "不支持该命令"; break; // 添加其他错误码处理... } final String finalMsg = errorMessage; runOnUiThread(() -> { Toast.makeText(MainActivity.this, (isStart ? "开始" : "停止") + "云台控制失败: " + finalMsg, Toast.LENGTH_LONG).show(); // 对于特定错误,重置云台状态 if ("10002".equals(code) || "60000".equals(code)) { isPTZActive = false; activeDirection = -1; } }); } @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) { // 竖屏模式 mRaTitle.setVisibility(View.VISIBLE); mRlControl.setVisibility(View.VISIBLE); mLlHc.setVisibility(View.GONE); mIbRotate2.setVisibility(View.GONE); } else { // 横屏模式 mRaTitle.setVisibility(View.GONE); 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, "P极取流已启用"); 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); } Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show(); // 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 (isPTZActive && activeDirection != -1) { Log.d(TAG, "Activity停止,强制停止云台"); controlPTZ(activeDirection, SPEED_MEDIUM, false); } if (mEZPlayer != null) { mEZPlayer.stopRealPlay(); mIsPlaying = false; // 标记为已停止 } } // 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); } if (!mHasShownConnectionSuccess) { mHasShownConnectionSuccess = true; } }); // 在播放成功后检查设备是否支持云台 checkDevicePTZSupport(); 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 void checkDevicePTZSupport() { new Thread(() -> { try { // 这里使用SDK方法检查设备能力 // 如果SDK没有提供方法,可以使用HTTP API查询设备能力 // 简化处理:假设所有设备都支持云台 mIsSupportPTZ = true; // 实际项目中应该查询设备能力 // mIsSupportPTZ = queryDevicePTZCapability(); } catch (Exception e) { Log.e(TAG, "Failed to check PTZ support", e); } }).start(); } @Override protected void onDestroy() { super.onDestroy(); if (mExecutorService != null) { mExecutorService.shutdown(); } // 移除Handler回调避免内存泄漏 if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } if (mEZPlayer != null) { mEZPlayer.release(); mEZPlayer = null; } } private String getStreamTypeName(int type) { switch (type) { case 0: return "流媒体"; case 1: return "P2P"; case 2: return "内网直连"; case 4: return "云存储"; default: return "未知(" + type + ")"; } } } FanHui.java: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.text.TextUtils; 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.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import android.content.res.Configuration; 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; import java.util.concurrent.TimeUnit; import android.content.pm.ActivityInfo; import com.videogo.widget.CheckTextButton; 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 = "deviceSerial"; 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 int MSG_PLAYBACK_STOP = 3003; // 更新API地址为云存储专用接口 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 boolean isFullScreen = false; private com.videogo.widget.TitleBar mTitleBar; private ViewGroup mPlaybackArea; private LinearLayout mControlArea; private RelativeLayout mDisplayLayout; private ImageButton mExitFullScreenButton; private ExecutorService mExecutorService; private ListView mListView; private PlaybackAdapter mAdapter; private List<VideoInfo> mVideoList = new ArrayList<>(); private LinearLayout mNoVideoLayout; // 播放器相关 private EZPlayer mEZPlayer; private SurfaceView mPlaybackSurfaceView; private SurfaceHolder mSurfaceHolder; private boolean isVideoPlaying = false; // 跟踪视频播放状态 private ImageButton mPauseButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ez_playback_list_page); View fanHui = findViewById(R.id.fanhui); fanHui.setOnClickListener(v -> finish()); // 创建线程池 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(); mPlaybackArea = findViewById(R.id.remote_playback_area); mControlArea = findViewById(R.id.control_area); mDisplayLayout = findViewById(R.id.display_layout); mExitFullScreenButton = findViewById(R.id.exit_btn); // 设置日期显示模块 setupDatePicker(); // 使用MainActivity已初始化的SDK实例 EZOpenSDK.getInstance().setAccessToken(mAccessToken); // 默认加载当天的录像 loadVideosForSelectedDate(); setupFullscreenButton(); mTitleBar = findViewById(R.id.title); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT ); mPlaybackArea.setLayoutParams(params); } } private void initViews() { // 查找ListView mListView = findViewById(R.id.listView); if (mListView == null) { Log.e(TAG, "ListView not found with ID listView"); return; } mNoVideoLayout = findViewById(R.id.novideo_img_cloud); // 初始化适配器 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); mPauseButton = findViewById(R.id.remote_playback_pause_btn); mPauseButton.setOnClickListener(v -> togglePlayPause()); if (mPlaybackSurfaceView != null) { // 设置SurfaceHolder回调 mSurfaceHolder = mPlaybackSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); } else { Log.e(TAG, "SurfaceView not found with ID remote_playback_wnd_sv"); } } private void togglePlayPause() { if (mEZPlayer == null) return; if (isVideoPlaying) { // 当前正在播放 → 暂停 try { mEZPlayer.pausePlayback(); mPauseButton.setImageResource(R.drawable.play_full_play_sel); // 切换为播放图标 isVideoPlaying = false; } catch (Exception e) { Log.e(TAG, "暂停播放失败", e); } } else { // 当前已暂停 → 继续播放 try { mEZPlayer.resumePlayback(); mPauseButton.setImageResource(R.drawable.play_full_pause_sel); // 切换为暂停图标 isVideoPlaying = true; } catch (Exception e) { Log.e(TAG, "继续播放失败", e); } } } // 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) { // 确保所有选择器组件可见 for (int i = 0; i < childView.getChildCount(); i++) { childView.getChildAt(i).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, "").toUpperCase(); 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 (Upper): " + 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() { if (TextUtils.isEmpty(mDeviceSerial) || TextUtils.isEmpty(mAccessToken)) { Toast.makeText(this, "设备参数不完整", Toast.LENGTH_SHORT).show(); return; } // 计算开始和结束时间戳 Calendar cal = Calendar.getInstance(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 0, 0, 0); long startTime = cal.getTimeInMillis(); long maxEndTime = System.currentTimeMillis(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 23, 59, 59); long endTime = Math.min(cal.getTimeInMillis(), maxEndTime); // 发起网络请求获取录像 fetchVideosByTime(startTime, endTime); } private void fetchVideosByTime(long startTime, long endTime) { if (isFinishing() || isDestroyed()) return; // 加载开始时隐藏无录像提示 runOnUiThread(() -> { if (mNoVideoLayout != null) { mNoVideoLayout.setVisibility(View.GONE); } if (!isFinishing()) { Toast.makeText(FanHui.this, "正在加载可播录像", Toast.LENGTH_SHORT).show(); } }); final String deviceSerial = mDeviceSerial.toUpperCase(); mExecutorService.execute(() -> { HttpURLConnection conn = null; try { String currentUrl = VIDEO_BY_TIME_URL; int redirectCount = 0; final int MAX_REDIRECTS = 3; while (redirectCount < MAX_REDIRECTS) { URL url = new URL(currentUrl); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(15000); conn.setReadTimeout(30000); // 构建POST数据 StringBuilder postData = new StringBuilder(); postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); postData.append("&deviceSerial=").append(URLEncoder.encode(deviceSerial, "UTF-8")); postData.append("&channelNo=").append(mCameraNo); postData.append("&startTime=").append(startTime); postData.append("&endTime=").append(endTime); postData.append("&recType=0"); // 1-云存储录像 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_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_SEE_OTHER) { String newUrl = conn.getHeaderField("Location"); if (newUrl == null) break; currentUrl = newUrl; redirectCount++; conn.disconnect(); continue; } // 处理正常响应 if (responseCode == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); // 明确指定UTF-8编码 StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); String responseString = response.toString(); Log.d(TAG, "API响应: " + responseString); // 记录原始响应 // 检查是否是HTML响应 if (responseString.startsWith("<html>") || responseString.startsWith("<!DOCTYPE")) { Log.e(TAG, "服务器返回HTML: " + responseString); runOnUiThread(() -> { if (!isFinishing()) { Toast.makeText(FanHui.this, "服务器返回错误页面", Toast.LENGTH_LONG).show(); } }); return; } // 解析JSON响应 try { JSONObject json = new JSONObject(responseString); String code = json.optString("code", "0"); if ("200".equals(code)) { Object dataObj = json.opt("data"); List<VideoInfo> videos = new ArrayList<>(); if (dataObj == null) { // 处理data为null的情况(无录像) Log.w(TAG, "API返回data为null,表示没有录像"); } else if (dataObj instanceof JSONArray) { // 处理数组格式响应 JSONArray dataArray = (JSONArray) dataObj; videos = parseVideoData(dataArray); } else if (dataObj instanceof JSONObject) { // 处理分页格式响应 JSONObject dataObject = (JSONObject) dataObj; // 使用 optJSONArray 安全处理 null 值 JSONArray filesArray = dataObject.optJSONArray("files"); if (filesArray != null) { videos = parseVideoData(filesArray); } else { JSONArray recordsArray = dataObject.optJSONArray("records"); if (recordsArray != null) { videos = parseVideoData(recordsArray); } else { JSONArray listArray = dataObject.optJSONArray("list"); if (listArray != null) { videos = parseVideoData(listArray); } else { Log.w(TAG, "分页结构缺少有效的录像数组字段"); // 尝试从其他可能字段获取 if (dataObject.has("videoList")) { videos = parseVideoData(dataObject.optJSONArray("videoList")); } else { Log.w(TAG, "API 返回的分页结构不包含有效录像数据: " + dataObject.toString()); } } } } } else { Log.w(TAG, "未知的data类型: " + dataObj.getClass().getSimpleName()); // 尝试强制转换 try { videos = parseVideoData(new JSONArray().put(dataObj)); } catch (Exception e) { Log.e(TAG, "强制转换失败", e); } } // 更新UI显示录像列表 List<VideoInfo> finalVideos = videos; runOnUiThread(() -> { mVideoList.clear(); mVideoList.addAll(finalVideos); mAdapter.notifyDataSetChanged(); // 根据录像数据控制提示布局可见性 if (finalVideos.isEmpty()) { Toast.makeText(FanHui.this, "该时间段没有录像", Toast.LENGTH_SHORT).show(); if (mNoVideoLayout != null) { mNoVideoLayout.setVisibility(View.VISIBLE); // 显示无录像提示 } } else { if (mNoVideoLayout != null) { mNoVideoLayout.setVisibility(View.GONE); // 隐藏无录像提示 } } }); } else { String msg = json.optString("msg", "未知错误"); Log.e(TAG, "获取录像失败: " + msg); runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像失败: " + msg, Toast.LENGTH_SHORT).show()); } } catch (JSONException e) { Log.e(TAG, "JSON解析错误: " + e.getMessage(), e); // 添加原始响应内容日志 Log.e(TAG, "原始响应内容: " + responseString); runOnUiThread(() -> Toast.makeText(FanHui.this, "数据格式错误: " + e.getMessage(), Toast.LENGTH_LONG).show()); } break; // 跳出重定向循环 } else { Log.e(TAG, "HTTP错误: " + responseCode); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } break; } } if (redirectCount >= MAX_REDIRECTS) { Log.e(TAG, "重定向次数超过限制"); runOnUiThread(() -> { if (!isFinishing()) { Toast.makeText(FanHui.this, "重定向次数过多", Toast.LENGTH_SHORT).show(); } }); } } catch (Exception e) { Log.e(TAG, "获取录像异常", e); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } } finally { if (conn != null) { conn.disconnect(); } } }); } 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(); // 兼容多种ID字段名 video.id = record.optString("id", record.optString("fileId", record.optString("recordId", "N/A"))); // 兼容多种开始时间字段 video.startTime = record.optLong("startTime", record.optLong("beginTime", record.optLong("start_time", 0))); // 兼容多种结束时间字段 video.endTime = record.optLong("endTime", record.optLong("stopTime", record.optLong("end_time", 0))); // 检查时间有效性 if (video.startTime == 0 || video.endTime == 0) { Log.w(TAG, "跳过无效时间段: start=" + video.startTime + ", end=" + video.endTime); continue; } // 格式化时间显示 try { Date startDate = new Date(video.startTime); Date endDate = new Date(video.endTime); video.timeRange = sdf.format(startDate) + " - " + sdf.format(endDate); videos.add(video); } catch (Exception e) { Log.e(TAG, "时间格式化失败", e); } } return videos; } private void playVideo(VideoInfo video) { // 停止当前播放 stopPlayback(); // 获取播放地址 playWithTimeRange(video); } private void playWithTimeRange(VideoInfo video) { try { // 创建播放器实例 mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // 设置回调处理器 mEZPlayer.setHandler(mHandler); // 设置验证码 mEZPlayer.setPlayVerifyCode(mVerifyCode); // 关联播放视图 if (mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } // 开始播放云存储录像 - 直接使用时间范围 Calendar startCal = Calendar.getInstance(); startCal.setTimeInMillis(video.startTime); Calendar endCal = Calendar.getInstance(); endCal.setTimeInMillis(video.endTime); // 开始播放云存储录像 - 使用Calendar对象 mEZPlayer.startPlayback(startCal, endCal); 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(); } isVideoPlaying = true; runOnUiThread(() -> { if (mPauseButton != null) { mPauseButton.setImageResource(R.drawable.play_full_pause_sel); } }); } private void setupFullscreenButton() { CheckTextButton fullscreenButton = findViewById(R.id.fullscreen_button); fullscreenButton.setOnClickListener(v -> toggleFullscreen()); // 确保退出按钮在横屏模式下可点击 mExitFullScreenButton.setOnClickListener(v -> toggleFullscreen()); mExitFullScreenButton.setClickable(true); // 明确设置可点击 } private void toggleFullscreen() { if (isFullScreen) { // 退出全屏 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); // 恢复UI布局 mControlArea.setVisibility(View.VISIBLE); mDisplayLayout.setVisibility(View.VISIBLE); mExitFullScreenButton.setVisibility(View.GONE); mTitleBar.setVisibility(View.VISIBLE); // 恢复播放器布局 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize(R.dimen.playback_height) // 300dp ); mPlaybackArea.setLayoutParams(params); } else { // 进入全屏 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); // 隐藏其他UI元素 mControlArea.setVisibility(View.GONE); mDisplayLayout.setVisibility(View.GONE); mExitFullScreenButton.setVisibility(View.VISIBLE); mTitleBar.setVisibility(View.GONE); // 设置播放器全屏 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT ); mPlaybackArea.setLayoutParams(params); // 强制SurfaceView重绘 if (mSurfaceHolder != null) { mSurfaceHolder.setFixedSize(getScreenWidth(), getScreenHeight()); } } isFullScreen = !isFullScreen; } private int getScreenWidth() { return getResources().getDisplayMetrics().widthPixels; } // 获取屏幕高度 private int getScreenHeight() { return getResources().getDisplayMetrics().heightPixels; } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 横屏时隐藏标题栏 if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { mTitleBar.setVisibility(View.GONE); // 确保播放器填充屏幕 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT ); mPlaybackArea.setLayoutParams(params); } else { mTitleBar.setVisibility(View.VISIBLE); // 恢复竖屏布局 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize(R.dimen.playback_height) // 300dp ); mPlaybackArea.setLayoutParams(params); } // 确保播放器正确重绘 if (mEZPlayer != null && mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } } private void stopPlayback() { if (mEZPlayer != null) { try { mEZPlayer.stopPlayback(); mEZPlayer.release(); } catch (Exception e) { Log.e(TAG, "停止播放失败", e); } mEZPlayer = null; } // 重置播放状态 isVideoPlaying = false; runOnUiThread(() -> { if (mPauseButton != null) { mPauseButton.setImageResource(R.drawable.play_full_pause_sel); } }); } // 播放器回调处理器 - 增强回调处理 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_PLAYBACK_PLAY_SUCCESS: Log.i(TAG, "回放播放成功"); Toast.makeText(FanHui.this, "回放播放成功", Toast.LENGTH_SHORT).show(); isVideoPlaying = true; // 确保状态同步 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无效"; } else if (errorCode == ErrorCode.ERROR_TRANSF_DEVICE_OFFLINE) { errorMsg = "设备离线"; } Toast.makeText(FanHui.this, errorMsg, Toast.LENGTH_LONG).show(); break; case MSG_PLAYBACK_STOP: Log.i(TAG, "回放停止"); isVideoPlaying = false; // 确保状态同步 break; } } }; @Override protected void onPause() { super.onPause(); stopPlayback(); shutdownExecutor(); } @Override protected void onResume() { super.onResume(); // 重新创建线程池 if (mExecutorService == null) { mExecutorService = Executors.newFixedThreadPool(2); } } @Override protected void onDestroy() { super.onDestroy(); stopPlayback(); shutdownExecutor(); } private void shutdownExecutor() { if (mExecutorService != null) { try { mExecutorService.shutdownNow(); if (!mExecutorService.awaitTermination(1, TimeUnit.SECONDS)) { Log.w(TAG, "线程池未能在超时时间内关闭"); } } catch (InterruptedException e) { Log.e(TAG, "线程池关闭被中断", e); Thread.currentThread().interrupt(); } mExecutorService = null; } } // 录像信息数据结构 public static class VideoInfo { String id; long startTime; long endTime; String timeRange; } // 列表适配器 public 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; } } } 用最简单的方式最少的代码修改量实现从MainActivity进入FanHui后,再次返回MainActivity时MainActivity继续播放直播预览视频。
06-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值