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.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;
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 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);
View fanHui = findViewById(R.id.back);
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();
// 设置日期显示模块
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, "").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) {
// 检查Activity状态
if (isFinishing() || isDestroyed()) return;
if (TextUtils.isEmpty(mDeviceSerial)) {
runOnUiThread(() -> Toast.makeText(FanHui.this,
"设备序列号为空", Toast.LENGTH_SHORT).show());
return;
}
// 确保序列号全大写
final String deviceSerial = mDeviceSerial.toUpperCase();
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(deviceSerial, "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();
if (videos.isEmpty()) {
Toast.makeText(FanHui.this,
"该时间段没有录像", Toast.LENGTH_SHORT).show();
}
});
}
} else {
String msg = json.optString("msg", "未知错误");
Log.e(TAG, "获取录像失败: " + msg +
" | DeviceSerial: " + deviceSerial +
" | CameraNo: " + mCameraNo +
" | StartTime: " + startTime +
" | EndTime: " + endTime);
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");
video.endTime = record.optLong("endTime");
// 格式化时间显示
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();
// 获取播放地址
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);
}
// 开始播放云存储录像 - 直接使用时间范围
mEZPlayer.startPlayback(video.startTime, video.endTime);
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 fetchPlaybackUrl(VideoInfo video) {
// // 检查Activity状态
// if (isFinishing() || isDestroyed()) return;
//
// if (TextUtils.isEmpty(mDeviceSerial)) {
// runOnUiThread(() -> Toast.makeText(FanHui.this,
// "设备序列号为空", Toast.LENGTH_SHORT).show());
// return;
// }
// final String deviceSerial = mDeviceSerial.toUpperCase();
//
// mExecutorService.execute(() -> {
// try {
// // 构建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("&type=3"); // 3-云存储回放
// 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 playbackUrl, VideoInfo video) {
// try {
// // 停止当前播放
// stopPlayback();
//
// // 创建播放器实例
// mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo);
//
// // 设置回调处理器
// mEZPlayer.setHandler(mHandler);
//
// // 设置验证码
// mEZPlayer.setPlayVerifyCode(mVerifyCode);
//
// // 关联播放视图
// if (mSurfaceHolder != null) {
// mEZPlayer.setSurfaceHold(mSurfaceHolder);
// }
//
// // 开始播放云存储录像 - 使用获取的播放地址
// mEZPlayer.startPlayback(video.startTime, video.endTime);
//
// 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) {
try {
mEZPlayer.stopPlayback();
mEZPlayer.release();
} catch (Exception e) {
Log.e(TAG, "停止播放失败", e);
}
mEZPlayer = null;
}
}
// 播放器回调处理器 - 增强回调处理
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();
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, "回放停止");
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;
}
}
}解决上述代码中报错:'startPlayback(java.util.Calendar, java.util.Calendar)' in 'com.videogo.openapi.EZPlayer' cannot be applied to '(long, long)'