Browse Source

fix: 修改无SDK的mac获取和APP更新

master
高志龙 3 months ago
parent
commit
e052d21b1a
  1. 3
      app/build.gradle
  2. 1
      app/src/main/java/qianmu/container/activity/program/ScreenSaverActivity.java
  3. 5
      app/src/main/java/qianmu/container/activity/program/ViewScreenSaver.java
  4. 3
      app/src/main/java/qianmu/container/app/Constant.java
  5. 60
      app/src/main/java/qianmu/container/mqtt/MQTTService.java
  6. 2
      app/src/main/java/qianmu/container/socket/SocketClient.java
  7. 32
      app/src/main/java/qianmu/container/util/AppUtil.java
  8. 64
      app/src/main/java/qianmu/container/util/BitmapUtil.java
  9. 144
      app/src/main/java/qianmu/container/util/DeviceUtil.java
  10. 25
      app/src/main/java/qianmu/container/util/SignWayUtil.java
  11. 265
      app/src/main/java/qianmu/container/view/CustomerVideoView.java
  12. 14
      app/src/main/res/xml/file_paths.xml

3
app/build.gradle

@ -12,7 +12,7 @@ android {
minSdkVersion 24
targetSdkVersion 30
versionCode 6
versionName "V2.0.8.19"
versionName "V2.0.8.20"
// 2.0.8.2
// 2.0.8.3 mac修改
// V2.0.8.5
@ -20,6 +20,7 @@ android {
// 2.0.8.12
//V2.0.8.14 5
//V2.0.8.15
//V2.0.8.20 SDK的mac获取和APP更新
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'

1
app/src/main/java/qianmu/container/activity/program/ScreenSaverActivity.java

@ -545,7 +545,6 @@ public class ScreenSaverActivity extends BaseActivity {
}
public void Screenshot(String time) {
String data3 = "{\"sn\":\"" + sn
+ "\",\"width\":1920,\"height\":1080,\"downLoadDirectoryPath\":\"/storage/emulated/0/\",\"pictureName\":\"screenShot.png\",\"pictureType\":1}";
instance.nvDownLoadScreenshotAsync(data3, new ViplexCore.CallBack() {

5
app/src/main/java/qianmu/container/activity/program/ViewScreenSaver.java

@ -635,7 +635,7 @@ public class ViewScreenSaver extends ViewBase<ViewScreenSaverBinding> {
return;
}
if (Constant.isMain) {
Log.e("TAG","主设备向其他设备发送图片下标"+imagePlayCount0);
LoggerUtil.e("TAG","主设备向其他设备发送图片下标"+imagePlayCount0);
SocketServerManager.sendMessageToClient(LocSocCliManager.PROGRAM_IMG_INDEX, String.valueOf(imagePlayCount0));
}
@ -2707,8 +2707,9 @@ public class ViewScreenSaver extends ViewBase<ViewScreenSaverBinding> {
binding.videoView0.setOnCompletionListener(new CustomerVideoView.OnCompletionListener() {
@Override
public void onCompletion() {
//判断如果是从设备且已连接,不切换
if(!LocSocCliManager.getLocalSocketState()){
// 视频播放完成时的操作
// 视频播放完成
handler.sendEmptyMessage(TYPE_UPDATE_VIDEO0);
}
}

3
app/src/main/java/qianmu/container/app/Constant.java

@ -22,7 +22,7 @@ public class Constant {
public static String screenType = "HDMI"; // 欣威视通3399设备为假关机 HDMI连接:可以用来判断是否为关机状态 LVDS连接:只能用定时关机时间来判断
public static String mqttState = ""; // 屏幕连接方式
public static String TTSHome="sbc"; // sbc-思必驰 kdxf-科大讯飞 (有语音的项目需要配置)
//public static String androidBoardType = ""; //设备板子型号 无固定版
// public static String androidBoardType = ""; //设备板子型号 无固定版
public static String androidBoardType = "ys"; // 设备板子型号 ys(亿晟) 北京颐堤港定制touch
// public static String androidBoardType = "xwst"; //设备板子型号 xwst(欣威视通3399)
// public static String androidBoardType = "xwst2"; //设备板子型号 xwst2(欣威视通3588、T982、3576)
@ -91,7 +91,6 @@ public class Constant {
public static final String NOVA_SCREEN_SHOT = "nova_screen_shot";// 诺瓦设备截屏
public static final String NOVA_SCREEN_POWER_ON = "nova_screen_power_on";// 诺瓦设备截屏
public static final String NOVA_SCREEN_POWER_CLOSE = "nova_screen_power_close";// 诺瓦设备截屏
public static final String APP_SEND_PACKAGE_NAME = "action_send_packageName";
/**

60
app/src/main/java/qianmu/container/mqtt/MQTTService.java

@ -9,13 +9,19 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import androidx.core.app.NotificationCompat;
@ -51,6 +57,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.security.KeyManagementException;
import java.security.KeyPair;
import java.security.KeyStore;
@ -674,52 +681,41 @@ public class MQTTService extends Service {
screenshotFuture = singleThreadExecutor.submit(() ->
SignWayUtil.ysTakeScreenshot(path)
);
}else if("huidu".equals(Constant.androidBoardType)){
}else if(!"".equals(Constant.androidBoardType)){
screenshotFuture = singleThreadExecutor.submit(() -> {
HuiduTech helper = new HuiduTech(MyApplication.getInstance());
helper.screenCapture(path);
return true;
});
}else if("bv".equals(Constant.androidBoardType)){
screenshotFuture = singleThreadExecutor.submit(() ->{
try{
Bitmap screenBitmap = MyApplication.getInstance().getLztek().screenCapture();
if(screenBitmap != null){
try (FileOutputStream fos = new FileOutputStream(file)) {
screenBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
if(new File(path).exists()){
LoggerUtil.e("screenShot","截屏完成");
return true;
}
}
}catch(Exception exp){
LoggerUtil.e("screenShot",exp.getMessage());
SignWayUtil.takeScreenshot(path);
if(new File(path).exists()){
LoggerUtil.e("screenShot","截屏完成");
return true;
}
return false;
});
}else{
screenshotFuture = singleThreadExecutor.submit(() -> {
Bitmap screenBitmap = null;
View decorView = MyApplication.getInstance().getCurrentActivity().getWindow().getDecorView();
if(decorView != null){
decorView.setDrawingCacheEnabled(true);
decorView.buildDrawingCache();
Bitmap bmp = decorView.getDrawingCache();
Rect frame = new Rect();
decorView.getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
Bitmap screenBitmap = Bitmap.createBitmap(bmp, 0, statusBarHeight, decorView.getWidth(), decorView.getHeight() - statusBarHeight);
decorView.setDrawingCacheEnabled(false);
//保存
//调用截屏
try {
decorView.setDrawingCacheEnabled(true);
decorView.buildDrawingCache();
screenBitmap = Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(screenBitmap);
decorView.draw(canvas);
decorView.setDrawingCacheEnabled(false);
} catch (Exception e) {
e.printStackTrace();
}
}
//保存
if(screenBitmap != null){
try (FileOutputStream fos = new FileOutputStream(file)) {
screenBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
screenBitmap.recycle();
if(new File(path).exists()){
LoggerUtil.e("screenShot","截屏完成");
return true;

2
app/src/main/java/qianmu/container/socket/SocketClient.java

@ -43,6 +43,7 @@ public class SocketClient extends WebSocketClient {
@Override
public void onClose(int code, String reason, boolean remote) {
if (closeListener != null) closeListener.onClose(code, reason, remote);
LoggerUtil.e("OnClose:", "socket断开");
}
@Override
@ -62,7 +63,6 @@ public class SocketClient extends WebSocketClient {
public void setOnCloseListener(OnCloseListener closeListener) {
this.closeListener = closeListener;
}
public void setOnErrorListener(OnErrorListener errorListener) {

32
app/src/main/java/qianmu/container/util/AppUtil.java

@ -8,6 +8,7 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import androidx.core.content.FileProvider;
@ -132,12 +133,37 @@ public class AppUtil {
* @param filePath
*/
public static void installApk(String filePath) {
//安装apk
File apkFile = new File(filePath);
if (!apkFile.exists()) {
// 文件不存在
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!MyApplication.getInstance().getPackageManager().canRequestPackageInstalls()) {
LoggerUtil.e("installApp: ", "安装未知来源APP1");
// 请求安装未知来源应用的权限
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + MyApplication.getInstance().getPackageName()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyApplication.getInstance().startActivity(intent);
return;
}
} else {
// 如果低于 Android 8.0,不处理
return;
}
LoggerUtil.e("installApp: ", "安装未知来源APP2");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
MyApplication.getInstance().startActivity(intent);
// 使用 FileProvider 获取 content URI
String authority = MyApplication.getInstance().getPackageName() + ".fileprovider";
Uri apkUri = FileProvider.getUriForFile(MyApplication.getInstance(), authority, apkFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
// 检查是否有应用可以处理该Intent
if (intent.resolveActivity(MyApplication.getInstance().getPackageManager()) != null) {
MyApplication.getInstance().startActivity(intent);
}
}
}

64
app/src/main/java/qianmu/container/util/BitmapUtil.java

@ -31,6 +31,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
import qianmu.container.R;
@ -329,4 +330,67 @@ public class BitmapUtil {
e.printStackTrace();
}
}
/** 横向拼接图片
*
* @param bitmaps
* @return
*/
public static Bitmap mergeBitmaps(List<Bitmap> bitmaps) {
if (bitmaps == null || bitmaps.isEmpty()) return null;
Bitmap bmp = bitmaps.get(0);
int width = bmp.getWidth();
int height = bmp.getHeight();
if(width > height){
return mergeBitmapsVertical(bitmaps);
}else{
return mergeBitmapsHorizontal(bitmaps);
}
}
/** 横向拼接图片
*
* @param bitmaps
* @return
*/
private static Bitmap mergeBitmapsHorizontal(List<Bitmap> bitmaps) {
if (bitmaps == null || bitmaps.isEmpty()) return null;
int totalWidth = 0;
int maxHeight = 0;
for (Bitmap bmp : bitmaps) {
totalWidth += bmp.getWidth();
maxHeight = Math.max(maxHeight, bmp.getHeight());
}
Bitmap result = Bitmap.createBitmap(totalWidth, maxHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
int currentX = 0;
for (Bitmap bmp : bitmaps) {
canvas.drawBitmap(bmp, currentX, 0, null);
currentX += bmp.getWidth();
}
return result;
}
/** 纵向拼接图片
*
* @param bitmaps
* @return
*/
private static Bitmap mergeBitmapsVertical(List<Bitmap> bitmaps) {
if (bitmaps == null || bitmaps.isEmpty()) return null;
int totalHeight = 0;
int maxWidth = 0;
for (Bitmap bmp : bitmaps) {
totalHeight += bmp.getHeight();
maxWidth = Math.max(maxWidth, bmp.getWidth());
}
Bitmap result = Bitmap.createBitmap(totalHeight, maxWidth, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
int currentY = 0;
for (Bitmap bmp : bitmaps) {
canvas.drawBitmap(bmp, 0, currentY, null);
currentY += bmp.getHeight();
}
return result;
}
}

144
app/src/main/java/qianmu/container/util/DeviceUtil.java

@ -30,7 +30,7 @@ import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;
import qianmu.container.app.Constant;
import qianmu.container.app.MyApplication;
import qianmu.container.data.DeviceData;
@ -169,62 +169,34 @@ public class DeviceUtil {
* 获取mac
* */
public static String getMacFromHardware() {
String mac= "";
try {
String Localmac = getLocalMacAddress();
if(Localmac!= null && !Localmac.equals("")){
mac = Localmac.toLowerCase();
}else{
mac = getWifiMacAddress();
}
LoggerUtil.e("mac获取","mac:"+mac);
String androidId = Settings.Secure.getString(
MyApplication.getInstance().getContentResolver(),
Settings.Secure.ANDROID_ID
);
String uniqueId = getCustomUniqueId(androidId);
LoggerUtil.e("mac获取", "Android唯一设备ID:" + uniqueId);
return uniqueId;
} catch (Exception e) {
mac="";
LoggerUtil.e("mac获取","mac获取失败"+e.getMessage());
LoggerUtil.e("mac获取", "获取唯一ID失败:" + e.getMessage());
}
return mac;
return null;
}
/**
* 根据IP地址获取MAC地址
* @return
* Android_ID 转成 类似 MAC 地址的格式xx:xx:xx:xx:xx:xx
*/
public static String getLocalMacAddress() {
String macAddress="";
macAddress = getMacFromNetworkInterface("wlan0");
if (!StringUtil.isEmpty(macAddress)) {
return macAddress;
private static String getCustomUniqueId(String androidId) {
if (androidId == null || androidId.length() < 12) {
return UUID.randomUUID().toString().replace("-", ":").substring(0, 13);
}
macAddress = getMacFromNetworkInterface("eth0");
if (!StringUtil.isEmpty(macAddress)) {
return macAddress;
}else{
macAddress = getLocalMacAddressFromIp();
}
return macAddress;
}
public static String getMacFromNetworkInterface(String interfaceName) {
try {
for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
if (interfaceName.equalsIgnoreCase(networkInterface.getName())) {
byte[] macBytes = networkInterface.getHardwareAddress();
if (macBytes != null) {
StringBuilder sb = new StringBuilder();
for (byte b : macBytes) {
sb.append(String.format("%02X:", b));
}
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}
}
} catch (Exception e) {
e.printStackTrace();
String raw = androidId.replaceAll("[^0-9A-Fa-f]", "").substring(0, 12);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < raw.length(); i += 2) {
sb.append(raw.substring(i, i + 2)).append(":");
}
return "";
if (sb.length() > 0) sb.deleteCharAt(sb.length() - 1);
return sb.toString().toUpperCase();
}
/**
@ -240,82 +212,6 @@ public class DeviceUtil {
}
}
/**
* 验证MAC地址格式
*/
private static boolean isValidMacAddress(String mac) {
if (StringUtil.isEmpty(mac)) {
return false;
}
// MAC地址正则表达式
String macPattern = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$";
return mac.matches(macPattern);
}
/**
* 格式化MAC地址
*/
private static String formatMacAddress(String mac) {
if (StringUtil.isEmpty(mac)) {
return "";
}
// 移除所有分隔符
String cleanMac = mac.replace(":", "").replace("-", "").replace(" ", "");
// 重新格式化为冒号分隔
if (cleanMac.length() == 12) {
StringBuilder formatted = new StringBuilder();
for (int i = 0; i < 12; i += 2) {
formatted.append(cleanMac.substring(i, i + 2));
if (i < 10) {
formatted.append(":");
}
}
return formatted.toString().toUpperCase();
}
return mac.toUpperCase();
}
/**
* 根据IP地址获取MAC地址
* @return
*/
public static String getLocalMacAddressFromIp() {
String strMacAddr = null;
try {
//获得IpD地址
InetAddress ip = getLocalInetAddress();
LoggerUtil.e("IP: ", ip.getHostAddress());
byte[] b = NetworkInterface.getByInetAddress(ip).getHardwareAddress();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < b.length; i++) {
if (i != 0) {
buffer.append(':');
}
String str = Integer.toHexString(b[i] & 0xFF);
buffer.append(str.length() == 1 ? 0 + str : str);
}
strMacAddr = buffer.toString().toUpperCase();
} catch (Exception e) {
}
return strMacAddr;
}
@SuppressLint("HardwareIds")
public static String getWifiMacAddress() {
try {
WifiManager wifiManager = (WifiManager) MyApplication.getInstance().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (wifiManager != null) {
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
return wifiInfo.getMacAddress();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取移动设备本地IP
* @return

25
app/src/main/java/qianmu/container/util/SignWayUtil.java

@ -2,6 +2,7 @@ package qianmu.container.util;
import android.app.smdt.SmdtManagerNew;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.RemoteException;
import android.os.signwaymanager.SignwayManager;
@ -17,6 +18,7 @@ import org.greenrobot.eventbus.EventBus;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
@ -282,12 +284,28 @@ public class SignWayUtil {
SignwayManager signwayManager = SignwayManager.getInstance(MyApplication.getInstance());
signwayManager.takeScreenshot(path, 0);
}else if(Constant.androidBoardType.equals("ys")){
Log.e("TAG","开始截屏");
MyManager manager = MyManager.getInstance(MyApplication.getInstance());
boolean b = manager.takeScreenshot(path);
Log.e("TAG","截屏结果"+b);
}else if("huidu".equals(Constant.androidBoardType)){
HuiduTech helper = new HuiduTech(MyApplication.getInstance());
helper.screenCapture(path);
}else if(Constant.androidBoardType.equals("smt")){
MyApplication.getInstance().getSmdt().disp_getScreenShot(path);
}else if("bv".equals(Constant.androidBoardType)){
try{
Bitmap screenBitmap = MyApplication.getInstance().getLztek().screenCapture();
if(screenBitmap != null){
try (FileOutputStream fos = new FileOutputStream(file)) {
screenBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}catch(Exception exp){
LoggerUtil.e("screenShot",exp.getMessage());
}
}
} catch (Throwable t) {
LoggerUtil.e("takeScreenshot", StringUtil.getThrowableStr(t));
}
@ -391,7 +409,6 @@ public class SignWayUtil {
zcApi.getContext(MyApplication.getInstance());
return zcApi.getEthMacAddress("eth0");
}else if(Constant.androidBoardType.equals("huidu")){
HuiduTech helper = new HuiduTech(MyApplication.getInstance());
if(!helper.getEthMac().isEmpty()){
String ethMac = helper.getEthMac();

265
app/src/main/java/qianmu/container/view/CustomerVideoView.java

@ -1,8 +1,8 @@
package qianmu.container.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
@ -11,7 +11,6 @@ import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.VideoView;
import androidx.annotation.NonNull;
@ -22,42 +21,24 @@ import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.ui.StyledPlayerView;
/**
* Created by Android Studio.
* User: linzhibin
* Date: 2021/5/19
* Time: 13:59
*/
public class CustomerVideoView extends FrameLayout {
private StyledPlayerView playerView;
private ExoPlayer player;
private TextureView mTextureView; // 我们自己管理 TextureView
private OnCompletionListener onCompletionListener;
private OnErrorListener onErrorListener;
private OnPreparedListener onPreparedListener;
private OnInfoListener onInfoListener;
private Boolean isFirstFame = false;
// 监听器接口,保持与 VideoView 兼容
public interface OnCompletionListener {
void onCompletion();
}
public interface OnErrorListener {
boolean onError();
}
public interface OnPreparedListener {
void onPrepared();
}
public interface OnInfoListener {
void onInfo();
}
// 接口保持不变
public interface OnCompletionListener { void onCompletion(); }
public interface OnErrorListener { boolean onError(); }
public interface OnPreparedListener { void onPrepared(); }
public interface OnInfoListener { void onInfo(); }
public CustomerVideoView(Context context) {
super(context);
@ -75,121 +56,103 @@ public class CustomerVideoView extends FrameLayout {
}
private void init(Context context) {
// 创建 StyledPlayerView
// 1. 创建 StyledPlayerView
playerView = new StyledPlayerView(context);
addView(playerView);
// 初始化 ExoPlayer
// 2. 初始化播放器
initializePlayer();
// 设置背景透明
// 透明设置
playerView.setBackgroundColor(Color.TRANSPARENT);
playerView.setShutterBackgroundColor(Color.TRANSPARENT);
// 3. 设置使用透明背景
playerView.setUseArtwork(false); // 如果不需要 artwork
playerView.setDefaultArtwork(null); // 清除默认 artwork
// 4. 设置 SurfaceView 透明
View videoSurfaceView = playerView.getVideoSurfaceView();
if (videoSurfaceView != null) {
videoSurfaceView.setBackgroundColor(Color.TRANSPARENT);
// 如果使用 SurfaceView,设置格式支持透明
if (videoSurfaceView instanceof SurfaceView) {
Log.e("View: ","SurfaceView");
SurfaceView surfaceView = (SurfaceView) videoSurfaceView;
replaceWithTextureView(playerView, (SurfaceView) surfaceView);
}else if (videoSurfaceView instanceof TextureView) {
// TextureView 默认支持透明
videoSurfaceView.setBackgroundColor(Color.TRANSPARENT);
}
}
playerView.setUseArtwork(false);
playerView.setDefaultArtwork(null);
replaceSurfaceWithTextureView();
}
private void replaceWithTextureView(StyledPlayerView playerView, SurfaceView oldSurface) {
ViewGroup parent = (ViewGroup) oldSurface.getParent();
if (parent != null) {
// 创建新的 TextureView
TextureView textureView = new TextureView(getContext());
textureView.setLayoutParams(oldSurface.getLayoutParams());
/**
* 安全替换 SurfaceView TextureView
*/
private void replaceSurfaceWithTextureView() {
if (playerView == null) return;
// 延迟获取,确保 View 已创建
playerView.post(() -> {
View videoView = playerView.getVideoSurfaceView();
if (!(videoView instanceof SurfaceView)) return;
SurfaceView oldSurface = (SurfaceView) videoView;
ViewGroup parent = (ViewGroup) oldSurface.getParent();
if (parent == null) return;
// 创建 TextureView
mTextureView = new TextureView(getContext());
mTextureView.setLayoutParams(oldSurface.getLayoutParams());
// 替换
int index = parent.indexOfChild(oldSurface);
parent.removeView(oldSurface);
parent.addView(textureView, index);
parent.addView(mTextureView, index);
// 设置到播放器
if (playerView.getPlayer() != null) {
playerView.getPlayer().setVideoTextureView(textureView);
// 绑定到播放器
if (player != null) {
player.setVideoTextureView(mTextureView);
}
}
});
}
private void initializePlayer() {
// 创建 ExoPlayer 实例
player = new SimpleExoPlayer.Builder(getContext())
.setTrackSelector(new DefaultTrackSelector(getContext()))
.build();
player.setPlayWhenReady(false);
// 设置播放器到 PlayerView
playerView.setPlayer(player);
setupPlayerListeners();
// 默认设置(可以根据需要调整)
playerView.setUseController(false);
// 默认设置 Surface 在底部
setZOrderOnTop(false);
setZOrderMediaOverlay(false);
}
private void setupPlayerListeners() {
if (player == null) return;
// 播放完成监听
player.addListener(new Player.Listener() {
@Override
public void onPlaybackStateChanged(int playbackState) {
if (playbackState == Player.STATE_ENDED) {
if (onCompletionListener != null) {
if (onCompletionListener != null)
onCompletionListener.onCompletion();
}
isFirstFame = false;
}
if(playbackState == Player.STATE_READY){
Log.e("loadVideo: ","STATE_READY");
// 视频准备就绪
if (onPreparedListener != null) {
if (playbackState == Player.STATE_READY) {
if (onPreparedListener != null)
onPreparedListener.onPrepared();
}
setVisibility(VISIBLE);
if(playerView != null && !isFirstFame){
if (playerView != null && !isFirstFame)
playerView.setAlpha(0f);
}
}
}
@Override
public void onPlayerError(@NonNull PlaybackException error) {
if (onErrorListener != null) {
if (onErrorListener != null)
onErrorListener.onError();
}
}
@Override
public void onRenderedFirstFrame() {
Log.e("loadVideo: ","FirstFrame");
if (onInfoListener != null) {
if (onInfoListener != null)
onInfoListener.onInfo();
}
isFirstFame = true;
if(playerView != null){
if (playerView != null)
playerView.setAlpha(1f);
}
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 保持原有的测量逻辑
int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width, height);
// 让 StyledPlayerView 填满整个父布局
if (playerView != null) {
playerView.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@ -201,151 +164,71 @@ public class CustomerVideoView extends FrameLayout {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// 布局 StyledPlayerView 使其填满整个控件
if (playerView != null) {
if (playerView != null)
playerView.layout(0, 0, getWidth(), getHeight());
}
}
// ==================== 实现 VideoView 的常用方法 ====================
// 1. setBackground - 设置背景
@Override
public void setBackground(Drawable background) {
super.setBackground(background);
// 如果需要设置 PlayerView 的背景
if (playerView != null) {
playerView.setVisibility(GONE);
}
if (playerView != null) playerView.setVisibility(GONE);
}
public void clearBackground(){
public void clearBackground() {
super.setBackground(null);
}
@Override
public void setBackgroundColor(int color) {
super.setBackgroundColor(color);
if (color != Color.TRANSPARENT && playerView != null) {
if (color != Color.TRANSPARENT && playerView != null)
playerView.setVisibility(GONE);
}
}
@Override
public void setBackgroundResource(int resid) {
super.setBackgroundResource(resid);
if (playerView != null) {
if (playerView != null)
playerView.setVisibility(INVISIBLE);
}
}
// 2. setZOrderOnTop - 设置 Surface 是否在最顶层
public void setZOrderOnTop(boolean onTop) {
if (playerView != null) {
// 获取 PlayerView 内部的 SurfaceView
View videoSurfaceView = playerView.getVideoSurfaceView();
if (videoSurfaceView instanceof SurfaceView) {
SurfaceView surfaceView = (SurfaceView) videoSurfaceView;
surfaceView.setZOrderOnTop(onTop);
}
}
}
public void setZOrderOnTop(boolean onTop) {}
public void setZOrderMediaOverlay(boolean isMediaOverlay) {}
// 3. setZOrderMediaOverlay - 设置 Surface 作为媒体叠加层
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
if (playerView != null) {
View videoSurfaceView = playerView.getVideoSurfaceView();
if (videoSurfaceView instanceof SurfaceView) {
SurfaceView surfaceView = (SurfaceView) videoSurfaceView;
surfaceView.setZOrderMediaOverlay(isMediaOverlay);
}
}
}
// 4. setVisibility - 设置可见性
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (playerView != null) {
if (playerView != null)
playerView.setVisibility(visibility);
}
}
// 5. 视频控制相关方法
public void setVideoPath(String path) {
if (player != null && path != null) {
try {
MediaItem mediaItem = MediaItem.fromUri(path);
player.setMediaItem(mediaItem);
player.prepare();
player.setPlaybackParameters(new PlaybackParameters(1.0f));
} catch (Exception e) {}
}
}
public void start() {
if (player != null) {
player.play();
}
}
public void pause() {
if (player != null) {
player.pause();
}
}
public void stopPlayback() {
if (player != null) {
player.stop();
}
}
public void seekTo(int position) {
if (player != null) {
player.seekTo(position);
MediaItem mediaItem = MediaItem.fromUri(path);
player.setMediaItem(mediaItem);
player.prepare();
player.setPlaybackParameters(new PlaybackParameters(1.0f));
}
}
public boolean isPlaying() {
return player != null && player.isPlaying();
}
// 6. 监听器设置
public void setOnCompletionListener(OnCompletionListener listener) {
this.onCompletionListener = listener;
}
public void setOnPreparedListener(OnPreparedListener listener) {
this.onPreparedListener = listener;
}
public void start() { if (player != null) player.play(); }
public void pause() { if (player != null) player.pause(); }
public void stopPlayback() { if (player != null) player.stop(); }
public void seekTo(int position) { if (player != null) player.seekTo(position); }
public boolean isPlaying() { return player != null && player.isPlaying(); }
public void setOnInfoListener(OnInfoListener listener) {
this.onInfoListener = listener;
}
public void setOnErrorListener(OnErrorListener listener) {
this.onErrorListener = listener;
}
// 可选:添加与 ExoPlayer 兼容的错误监听器
public void setOnExoPlayerErrorListener(Player.Listener errorListener) {
if (player != null && errorListener != null) {
player.addListener(errorListener);
}
}
public void setOnCompletionListener(OnCompletionListener listener) { this.onCompletionListener = listener; }
public void setOnPreparedListener(OnPreparedListener listener) { this.onPreparedListener = listener; }
public void setOnInfoListener(OnInfoListener listener) { this.onInfoListener = listener; }
public void setOnErrorListener(OnErrorListener listener) { this.onErrorListener = listener; }
public void release() {
if (player != null) {
player.setVideoTextureView(null);
player.release();
player = null;
}
if (playerView != null) {
playerView.setPlayer(null);
}
onCompletionListener = null;
onErrorListener = null;
if (playerView != null) playerView.setPlayer(null);
}
@Override
@ -354,7 +237,6 @@ public class CustomerVideoView extends FrameLayout {
release();
}
// 重置播放器
public void reset() {
if (player != null) {
player.stop();
@ -362,12 +244,7 @@ public class CustomerVideoView extends FrameLayout {
}
}
// 设置音量
public void setVolume(float volume) {
if (player != null) {
player.setVolume(volume);
}
if (player != null) player.setVolume(volume);
}
}
}

14
app/src/main/res/xml/file_paths.xml

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 根据你的需求添加路径 -->
<external-path name="external_files" path="." />
<files-path name="internal_files" path="." />
<!-- 外部存储根目录 -->
<external-path name="external_root" path="." />
<!-- 外部存储应用私有目录 -->
<external-files-path name="external_files" path="." />
<!-- 内部存储根目录 -->
<root-path name="root" path="." />
<!-- 外部存储缓存目录 -->
<cache-path name="cache" path="." />
<!-- 外部存储文件目录 -->
<files-path name="files" path="." />
<files-path name="internal_files" path="." />
</paths>
Loading…
Cancel
Save