File file1_str = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera1/1.ini"); 如何向1.ini写入内容和读取内容

时间: 2025-07-29 11:35:54 浏览: 5
<think>我们使用标准的JavaI/O流操作来写入和读取文件内容。由于文件位于外部存储,需要确保已经获取了外部存储的写入权限(在AndroidManifest.xml中声明WRITE_EXTERNAL_STORAGE权限,并且在Android6.0及以上版本动态请求权限)。步骤:1.检查外部存储是否可用(可写)。2.使用FileOutputStream写入内容。3.使用FileInputStream读取内容。注意:在Android10及以上版本,由于分区存储(ScopedStorage)的限制,访问外部存储的方式有所变化。如果目标API级别>=29,可能需要使用MediaStore或者其他方法。但这里我们假设应用有权限访问该路径(例如,通过requestLegacyExternalStorage或者应用的目标SDK版本低于29)。以下是写入和读取文件的代码示例:</think>### 在 Android 中读写外部存储中的 INI 文件 以下是完整的解决方案,用于在 Android 设备的外部存储(如 `/sdcard/DCIM/Camera1/1.ini`)中读写 INI 文件内容。INI 文件是一种简单的配置文件格式,通常包含键值对。 #### 1. 权限配置 首先在 `AndroidManifest.xml` 中添加必要的权限: ```xml <!-- 外部存储读写权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Android 10+ 需要额外声明 --> <application android:requestLegacyExternalStorage="true" ...> </application> ``` #### 2. 动态权限请求(Android 6.0+) ```java public class MainActivity extends AppCompatActivity { private static final int REQUEST_STORAGE_PERMISSION = 1; private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera1/1.ini"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查并请求权限 if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions( new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_STORAGE_PERMISSION ); } else { initFileOperations(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { initFileOperations(); } else { Toast.makeText(this, "需要存储权限才能操作文件", Toast.LENGTH_SHORT).show(); } } } private void initFileOperations() { // 初始化文件操作 File iniFile = new File(FILE_PATH); // 写入示例 Button writeBtn = findViewById(R.id.writeBtn); writeBtn.setOnClickListener(v -> { Map<String, String> data = new HashMap<>(); data.put("resolution", "1920x1080"); data.put("quality", "high"); data.put("last_modified", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date())); writeIniFile(iniFile, data); }); // 读取示例 Button readBtn = findViewById(R.id.readBtn); readBtn.setOnClickListener(v -> { Map<String, String> content = readIniFile(iniFile); StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : content.entrySet()) { sb.append(entry.getKey()).append(" = ").append(entry.getValue()).append("\n"); } TextView resultView = findViewById(R.id.resultView); resultView.setText(sb.toString()); }); } } ``` #### 3. INI 文件读写工具类 ```java public class IniFileUtils { // 写入 INI 文件 public static boolean writeIniFile(File file, Map<String, String> data) { // 确保目录存在 File parentDir = file.getParentFile(); if (!parentDir.exists() && !parentDir.mkdirs()) { Log.e("IniFileUtils", "无法创建目录: " + parentDir.getAbsolutePath()); return false; } try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { for (Map.Entry<String, String> entry : data.entrySet()) { // 写入键值对:key=value writer.write(entry.getKey() + "=" + entry.getValue()); writer.newLine(); } writer.flush(); return true; } catch (IOException e) { Log.e("IniFileUtils", "写入文件失败: " + file.getAbsolutePath(), e); return false; } } // 读取 INI 文件 public static Map<String, String> readIniFile(File file) { Map<String, String> result = new HashMap<>(); if (!file.exists()) { Log.w("IniFileUtils", "文件不存在: " + file.getAbsolutePath()); return result; } try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; while ((line = reader.readLine()) != null) { // 跳过空行和注释 if (line.trim().isEmpty() || line.trim().startsWith("#") || line.trim().startsWith(";")) { continue; } // 解析键值对 int separatorIndex = line.indexOf('='); if (separatorIndex > 0) { String key = line.substring(0, separatorIndex).trim(); String value = line.substring(separatorIndex + 1).trim(); result.put(key, value); } } } catch (IOException e) { Log.e("IniFileUtils", "读取文件失败: " + file.getAbsolutePath(), e); } return result; } // 更新特定键的值 public static boolean updateIniValue(File file, String key, String newValue) { Map<String, String> currentData = readIniFile(file); currentData.put(key, newValue); return writeIniFile(file, currentData); } // 删除特定键 public static boolean removeIniKey(File file, String key) { Map<String, String> currentData = readIniFile(file); if (currentData.containsKey(key)) { currentData.remove(key); return writeIniFile(file, currentData); } return false; } } ``` #### 4. 处理 Android 10+ 的分区存储 (Scoped Storage) 对于 Android 10 (API 29) 及以上版本,需要使用 MediaStore 或 SAF 访问文件: ```java // 使用 MediaStore 写入文件 (Android 10+) public static boolean writeIniFileWithMediaStore(Context context, String fileName, Map<String, String> data) { ContentResolver resolver = context.getContentResolver(); ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain"); values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/Camera1"); try { Uri uri = resolver.insert(MediaStore.Files.getContentUri("external"), values); if (uri == null) return false; try (OutputStream os = resolver.openOutputStream(uri); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os))) { for (Map.Entry<String, String> entry : data.entrySet()) { writer.write(entry.getKey() + "=" + entry.getValue()); writer.newLine(); } writer.flush(); return true; } } catch (IOException e) { Log.e("IniFileUtils", "MediaStore写入失败", e); return false; } } // 使用 MediaStore 读取文件 (Android 10+) public static Map<String, String> readIniFileWithMediaStore(Context context, String fileName) { Map<String, String> result = new HashMap<>(); ContentResolver resolver = context.getContentResolver(); Uri collection = MediaStore.Files.getContentUri("external"); String[] projection = { MediaStore.Files.FileColumns._ID }; String selection = MediaStore.Files.FileColumns.RELATIVE_PATH + "=? AND " + MediaStore.Files.FileColumns.DISPLAY_NAME + "=?"; String[] selectionArgs = new String[] { Environment.DIRECTORY_DCIM + "/Camera1", fileName }; try (Cursor cursor = resolver.query(collection, projection, selection, selectionArgs, null)) { if (cursor != null && cursor.moveToFirst()) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)); Uri fileUri = ContentUris.withAppendedId(collection, id); try (InputStream is = resolver.openInputStream(fileUri); BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { String line; while ((line = reader.readLine()) != null) { if (line.trim().isEmpty() || line.trim().startsWith("#")) continue; int separatorIndex = line.indexOf('='); if (separatorIndex > 0) { String key = line.substring(0, separatorIndex).trim(); String value = line.substring(separatorIndex + 1).trim(); result.put(key, value); } } } } } catch (IOException e) { Log.e("IniFileUtils", "MediaStore读取失败", e); } return result; } ``` #### 5. 高级 INI 文件处理(支持节) 对于更复杂的 INI 文件(包含节): ```java // 支持节的 INI 文件结构 public static Map<String, Map<String, String>> readIniFileWithSections(File file) { Map<String, Map<String, String>> result = new LinkedHashMap<>(); Map<String, String> currentSection = null; if (!file.exists()) return result; try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; while ((line = reader.readLine()) != null) { line = line.trim(); // 跳过空行和注释 if (line.isEmpty() || line.startsWith("#") || line.startsWith(";")) { continue; } // 解析节 [section] if (line.startsWith("[") && line.endsWith("]")) { String sectionName = line.substring(1, line.length() - 1).trim(); currentSection = new LinkedHashMap<>(); result.put(sectionName, currentSection); continue; } // 解析键值对 if (currentSection != null) { int separatorIndex = line.indexOf('='); if (separatorIndex > 0) { String key = line.substring(0, separatorIndex).trim(); String value = line.substring(separatorIndex + 1).trim(); currentSection.put(key, value); } } } } catch (IOException e) { Log.e("IniFileUtils", "读取带节的文件失败", e); } return result; } // 写入带节的 INI 文件 public static boolean writeIniFileWithSections(File file, Map<String, Map<String, String>> data) { File parentDir = file.getParentFile(); if (!parentDir.exists() && !parentDir.mkdirs()) { return false; } try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { for (Map.Entry<String, Map<String, String>> section : data.entrySet()) { // 写入节头 writer.write("[" + section.getKey() + "]"); writer.newLine(); // 写入键值对 for (Map.Entry<String, String> entry : section.getValue().entrySet()) { writer.write(entry.getKey() + " = " + entry.getValue()); writer.newLine(); } // 节之间空行 writer.newLine(); } return true; } catch (IOException e) { Log.e("IniFileUtils", "写入带节的文件失败", e); return false; } } ``` #### 6. 文件监控和自动重载 ```java public class IniFileWatcher { private final File file; private final OnFileChangedListener listener; private FileObserver fileObserver; private long lastModified = 0; public interface OnFileChangedListener { void onFileChanged(Map<String, String> newContent); } public IniFileWatcher(File file, OnFileChangedListener listener) { this.file = file; this.listener = listener; startWatching(); } private void startWatching() { if (fileObserver != null) return; fileObserver = new FileObserver(file.getParentFile().getAbsolutePath()) { @Override public void onEvent(int event, String path) { if (path != null && path.equals(file.getName())) { if ((event & FileObserver.MODIFY) != 0) { long currentModified = file.lastModified(); if (currentModified > lastModified) { lastModified = currentModified; reloadFile(); } } } } }; fileObserver.startWatching(); reloadFile(); // 初始加载 } private void reloadFile() { new Thread(() -> { Map<String, String> content = IniFileUtils.readIniFile(file); new Handler(Looper.getMainLooper()).post(() -> { listener.onFileChanged(content); }); }).start(); } public void stopWatching() { if (fileObserver != null) { fileObserver.stopWatching(); fileObserver = null; } } } // 使用示例 public class MainActivity extends AppCompatActivity { private IniFileWatcher fileWatcher; @Override protected void onCreate(Bundle savedInstanceState) { // ... 其他代码 ... File iniFile = new File(FILE_PATH); fileWatcher = new IniFileWatcher(iniFile, newContent -> { // 当文件变化时更新UI TextView resultView = findViewById(R.id.resultView); StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : newContent.entrySet()) { sb.append(entry.getKey()).append(" = ").append(entry.getValue()).append("\n"); } resultView.setText(sb.toString()); }); } @Override protected void onDestroy() { super.onDestroy(); if (fileWatcher != null) { fileWatcher.stopWatching(); } } } ``` ### 关键注意事项 1. **权限处理**: - Android 6.0+ 需要动态请求权限 - Android 10+ 需要处理分区存储 - Android 11+ 需要 MANAGE_EXTERNAL_STORAGE 权限 2. **文件路径**: - 使用 `Environment.getExternalStorageDirectory()` 获取外部存储根目录 - 考虑不同设备的路径差异 - 使用 `Context.getExternalFilesDir()` 获取应用专属外部存储 3. **错误处理**: - 检查文件是否存在 - 处理 IO 异常 - 处理空文件或无效格式 4. **性能优化**: - 大文件使用缓冲读写 - 避免主线程文件操作 - 使用缓存减少重复读取 5. **线程安全**: ```java private static final Object fileLock = new Object(); public static boolean writeIniFile(File file, Map<String, String> data) { synchronized (fileLock) { // 文件操作代码 } } ``` ### 完整示例:INI 配置管理器 ```java public class IniConfigManager { private final File configFile; private Map<String, String> cachedConfig; private long lastLoadTime; public IniConfigManager(String filePath) { this.configFile = new File(filePath); loadConfig(); } public String getValue(String key, String defaultValue) { synchronized (this) { if (cachedConfig == null || configFile.lastModified() > lastLoadTime) { loadConfig(); } return cachedConfig.getOrDefault(key, defaultValue); } } public boolean setValue(String key, String value) { synchronized (this) { if (cachedConfig == null) loadConfig(); cachedConfig.put(key, value); return writeConfig(); } } public boolean removeKey(String key) { synchronized (this) { if (cachedConfig == null) loadConfig(); if (cachedConfig.remove(key) != null) { return writeConfig(); } return false; } } private void loadConfig() { cachedConfig = IniFileUtils.readIniFile(configFile); lastLoadTime = System.currentTimeMillis(); } private boolean writeConfig() { boolean success = IniFileUtils.writeIniFile(configFile, cachedConfig); if (success) { lastLoadTime = configFile.lastModified(); } return success; } } ``` ###
阅读全文

相关推荐

package com.example.vcam; import android.Manifest; import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.SurfaceTexture; //import android.hardware.Camera; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.InputConfiguration; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; import android.media.MediaPlayer; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import android.widget.Toast; import com.example.vcam.event.EventProxyHandler; import com.example.vcam.event.SomeEvent; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; public class HookMain implements IXposedHookLoadPackage { public static Surface mSurface; public static SurfaceTexture mSurfacetexture; public static MediaPlayer mMediaPlayer; public static SurfaceTexture fake_SurfaceTexture; //public static Camera origin_preview_camera; //public static Camera camera_onPreviewFrame; //public static Camera start_preview_camera; public static volatile byte[] data_buffer = {0}; public static byte[] input; public static int mhight; public static int mwidth; public static boolean is_someone_playing; public static boolean is_hooked; public static VideoToFrames hw_decode_obj; public static VideoToFrames c2_hw_decode_obj; public static VideoToFrames c2_hw_decode_obj_1; public static SurfaceTexture c1_fake_texture; public static Surface c1_fake_surface; public static SurfaceHolder ori_holder; public static MediaPlayer mplayer1; //public static Camera mcamera1; public int imageReaderFormat = 0; public static boolean is_first_hook_build = true; public static int onemhight; public static int onemwidth; public static Class camera_callback_calss; public static String video_path = "/storage/emulated/0/DCIM/Camera1/"; public static Surface c2_preview_Surfcae; public static Surface c2_preview_Surfcae_1; public static Surface c2_reader_Surfcae; public static Surface c2_reader_Surfcae_1; public static MediaPlayer c2_player; public static MediaPlayer c2_player_1; public static Surface c2_virtual_surface; public static SurfaceTexture c2_virtual_surfaceTexture; public boolean need_recreate; public static CameraDevice.StateCallback c2_state_cb; public static CaptureRequest.Builder c2_builder; public static SessionConfiguration fake_sessionConfiguration; public static SessionConfiguration sessionConfiguration; public static OutputConfiguration outputConfiguration; public boolean need_to_show_toast = true; private static Context appContext; public int c2_ori_width = 1280; public int c2_ori_height = 720; public static Class c2_state_callback; public Context toast_content; private static ClassLoader hostClassLoader; // 视频源管理器 //public static VideoSourceManager videoSourceManager = new VideoSourceManager(); VideoSourceManager manager = VideoSourceManager.getInstance(); public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Exception { // 初始化视频源管理器 if (manager == null) { manager = new VideoSourceManager(); } // 添加默认视频源 String defaultVideo = video_path + "virtual.mp4"; manager.addVideoSource("default", defaultVideo); manager.setCurrentSource("default"); if (lpparam.packageName.equals("com.example.vcam")){ XposedBridge.log("【VCAM】event2 " + lpparam.packageName); hostClassLoader = lpparam.classLoader; Class<?> mainActivityClass = hostClassLoader.loadClass( "com.example.vcam.MainActivity" ); Class<?> iEventInterface = hostClassLoader.loadClass( "com.example.vcam.IEvent" ); XposedHelpers.findAndHookMethod(mainActivityClass,"onEvent", iEventInterface,new XC_MethodHook(){ @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { //XposedBridge.log("【VCAM】event2 onEvent" ); Object event = param.args[0]; // EventProxyHandler.handleEvent( // event, // event.getClass() // 使用实际运行时类 // ); Method getMessage = iEventInterface.getMethod("getMessage"); String MessageStr = (String) getMessage.invoke(event); Method getBeiCan1 = iEventInterface.getMethod("getBeiCan1"); String BeiCan1Str = (String) getBeiCan1.invoke(event); XposedBridge.log("【VCAM】event " + MessageStr); XposedBridge.log("【VCAM】eventpath " + BeiCan1Str); if (MessageStr == "qiehuan1") { String sourceName = "source_virtual21"; // 添加新视频源(如果不存在) if (!manager.videoSources.containsKey(sourceName)) { manager.addVideoSource(sourceName, BeiCan1Str); } // 切换到新视频源 manager.switchToSource(sourceName); // 通知相机重启 manager.onCameraRestart(); // 强制重启相机 forceRestartCamera(lpparam); } } }); } XposedHelpers.findAndHookMethod("android.hardware.camera2.CameraManager", lpparam.classLoader, "openCamera", String.class, CameraDevice.StateCallback.class, Handler.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { if (param.args[1] == null) { return; } if (param.args[1].equals(c2_state_cb)) { return; } c2_state_cb = (CameraDevice.StateCallback) param.args[1]; c2_state_callback = param.args[1].getClass(); File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg"); if (control_file.exists()) { return; } File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists()) { if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径1:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee.toString()); } } return; } XposedBridge.log("【VCAM】1位参数初始化相机,类:" + c2_state_callback.getName().toString()); is_first_hook_build = true; process_camera2_init(c2_state_callback); } }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { XposedHelpers.findAndHookMethod("android.hardware.camera2.CameraManager", lpparam.classLoader, "openCamera", String.class, Executor.class, CameraDevice.StateCallback.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】2位参数初始化相机,类:" + c2_state_callback.toString()); if (param.args[2] == null) { return; } if (param.args[2].equals(c2_state_cb)) { return; } c2_state_cb = (CameraDevice.StateCallback) param.args[2]; File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg"); if (control_file.exists()) { return; } File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists()) { if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径2:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee.toString()); } } return; } c2_state_callback = param.args[2].getClass(); XposedBridge.log("【VCAM】2位参数初始化相机,类:" + c2_state_callback.toString()); is_first_hook_build = true; process_camera2_init(c2_state_callback); } }); } // XposedHelpers.findAndHookMethod("android.media.MediaRecorder", lpparam.classLoader, "setCamera", Camera.class, new XC_MethodHook() { // @Override // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // super.beforeHookedMethod(param); // File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); // need_to_show_toast = !toast_control.exists(); // XposedBridge.log("【VCAM】[record]" + lpparam.packageName); // if (toast_content != null && need_to_show_toast) { // try { // Toast.makeText(toast_content, "应用:" + lpparam.appInfo.name + "(" + lpparam.packageName + ")" + "触发了录像,但目前无法拦截", Toast.LENGTH_SHORT).show(); // }catch (Exception ee){ // XposedBridge.log("【VCAM】[toast]" + Arrays.toString(ee.getStackTrace())); // } // } // } // }); XposedHelpers.findAndHookMethod("android.app.Instrumentation", lpparam.classLoader, "callApplicationOnCreate", Application.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); if (param.args[0] instanceof Application) { try { toast_content = ((Application) param.args[0]).getApplicationContext(); } catch (Exception ee) { XposedBridge.log("【VCAM】" + ee.toString()); } File force_private = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera1/private_dir.jpg"); if (toast_content != null) {//后半段用于强制私有目录 int auth_statue = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { try { auth_statue += (toast_content.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + 1); } catch (Exception ee) { XposedBridge.log("【VCAM】[permission-check]" + ee.toString()); } try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { auth_statue += (toast_content.checkSelfPermission(Manifest.permission.MANAGE_EXTERNAL_STORAGE) + 1); } } catch (Exception ee) { XposedBridge.log("【VCAM】[permission-check]" + ee.toString()); } }else { if (toast_content.checkCallingPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ){ auth_statue = 2; } } //权限判断完毕 if (auth_statue < 1 || force_private.exists()) { File shown_file = new File(toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/"); if ((!shown_file.isDirectory()) && shown_file.exists()) { shown_file.delete(); } if (!shown_file.exists()) { shown_file.mkdir(); } shown_file = new File(toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/" + "has_shown"); File toast_force_file = new File(Environment.getExternalStorageDirectory().getPath()+ "/DCIM/Camera1/force_show.jpg"); if ((!lpparam.packageName.equals(BuildConfig.APPLICATION_ID)) && ((!shown_file.exists()) || toast_force_file.exists())) { try { Toast.makeText(toast_content, lpparam.packageName+"未授予读取本地目录权限,请检查权限\nCamera1目前重定向为 " + toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/", Toast.LENGTH_SHORT).show(); FileOutputStream fos = new FileOutputStream(toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/" + "has_shown"); String info = "shown"; fos.write(info.getBytes()); fos.flush(); fos.close(); } catch (Exception e) { XposedBridge.log("【VCAM】[switch-dir]" + e.toString()); } } video_path = toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/"; }else { video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/"; } } else { video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/"; File uni_DCIM_path = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/"); if (uni_DCIM_path.canWrite()) { File uni_Camera1_path = new File(video_path); if (!uni_Camera1_path.exists()) { uni_Camera1_path.mkdir(); } } } } } }); XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "addTarget", Surface.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { if (param.args[0] == null) { return; } if (param.thisObject == null) { return; } File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists()) { if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径3:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee.toString()); } } return; } if (param.args[0].equals(c2_virtual_surface)) { return; } File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg"); if (control_file.exists()) { return; } String surfaceInfo = param.args[0].toString(); if (surfaceInfo.contains("Surface(name=null)")) { if (c2_reader_Surfcae == null) { c2_reader_Surfcae = (Surface) param.args[0]; } else { if ((!c2_reader_Surfcae.equals(param.args[0])) && c2_reader_Surfcae_1 == null) { c2_reader_Surfcae_1 = (Surface) param.args[0]; } } } else { if (c2_preview_Surfcae == null) { c2_preview_Surfcae = (Surface) param.args[0]; } else { if ((!c2_preview_Surfcae.equals(param.args[0])) && c2_preview_Surfcae_1 == null) { c2_preview_Surfcae_1 = (Surface) param.args[0]; } } } XposedBridge.log("【VCAM】添加目标:" + param.args[0].toString()); param.args[0] = c2_virtual_surface; } }); XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "addTarget", Surface.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Surface surface = (Surface) param.args[0]; VideoSourceManager.getInstance().prepareForSurface(surface); } }); XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "removeTarget", Surface.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { if (param.args[0] == null) { return; } if (param.thisObject == null) { return; } File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists()) { if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径4:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee.toString()); } } return; } File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg"); if (control_file.exists()) { return; } Surface rm_surf = (Surface) param.args[0]; if (rm_surf.equals(c2_preview_Surfcae)) { c2_preview_Surfcae = null; } if (rm_surf.equals(c2_preview_Surfcae_1)) { c2_preview_Surfcae_1 = null; } if (rm_surf.equals(c2_reader_Surfcae_1)) { c2_reader_Surfcae_1 = null; } if (rm_surf.equals(c2_reader_Surfcae)) { c2_reader_Surfcae = null; } XposedBridge.log("【VCAM】移除目标:" + param.args[0].toString()); } }); XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "build", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { if (param.thisObject == null) { return; } if (param.thisObject.equals(c2_builder)) { return; } c2_builder = (CaptureRequest.Builder) param.thisObject; File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists() && need_to_show_toast) { if (toast_content != null) { try { Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径5:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee.toString()); } } return; } File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg"); if (control_file.exists()) { return; } XposedBridge.log("【VCAM】开始build请求"); process_camera2_play(); } }); XposedHelpers.findAndHookMethod("android.media.ImageReader", lpparam.classLoader, "newInstance", int.class, int.class, int.class, int.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { XposedBridge.log("【VCAM】应用创建了渲染器:宽:" + param.args[0] + " 高:" + param.args[1] + "格式" + param.args[2]); c2_ori_width = (int) param.args[0]; c2_ori_height = (int) param.args[1]; imageReaderFormat = (int) param.args[2]; File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "应用创建了渲染器:\n宽:" + param.args[0] + "\n高:" + param.args[1] + "\n一般只需要宽高比与视频相同", Toast.LENGTH_SHORT).show(); } catch (Exception e) { XposedBridge.log("【VCAM】[toast]" + e.toString()); } } } }); XposedHelpers.findAndHookMethod("android.hardware.camera2.CameraCaptureSession.CaptureCallback", lpparam.classLoader, "onCaptureFailed", CameraCaptureSession.class, CaptureRequest.class, CaptureFailure.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { XposedBridge.log("【VCAM】onCaptureFailed" + "原因:" + ((CaptureFailure) param.args[2]).getReason()); } }); } private void process_camera2_play() { if (c2_reader_Surfcae != null) { XposedBridge.log("【VCAM c2_reader_Surfcae 非空】" ); if (c2_hw_decode_obj != null) { c2_hw_decode_obj.stopDecode(); c2_hw_decode_obj = null; } c2_hw_decode_obj = new VideoToFrames(); try { if (imageReaderFormat == 256) { c2_hw_decode_obj.setSaveFrames("null", OutputImageFormat.JPEG); } else { c2_hw_decode_obj.setSaveFrames("null", OutputImageFormat.NV21); } c2_hw_decode_obj.set_surfcae(c2_reader_Surfcae); c2_hw_decode_obj.decode(video_path + "virtual.mp4"); } catch (Throwable throwable) { XposedBridge.log("【VCAM】" + throwable); } } if (c2_reader_Surfcae_1 != null) { XposedBridge.log("【VCAM c2_reader_Surfcae_1 非空】" ); if (c2_hw_decode_obj_1 != null) { c2_hw_decode_obj_1.stopDecode(); c2_hw_decode_obj_1 = null; } c2_hw_decode_obj_1 = new VideoToFrames(); try { if (imageReaderFormat == 256) { c2_hw_decode_obj_1.setSaveFrames("null", OutputImageFormat.JPEG); } else { c2_hw_decode_obj_1.setSaveFrames("null", OutputImageFormat.NV21); } c2_hw_decode_obj_1.set_surfcae(c2_reader_Surfcae_1); c2_hw_decode_obj_1.decode(video_path + "virtual.mp4"); } catch (Throwable throwable) { XposedBridge.log("【VCAM】" + throwable); } } if (c2_preview_Surfcae != null) { XposedBridge.log("【VCAM c2_preview_Surfcae 非空】" ); if (c2_player == null) { c2_player = new MediaPlayer(); } else { c2_player.release(); c2_player = new MediaPlayer(); } c2_player.setSurface(c2_preview_Surfcae); File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no-silent.jpg"); if (!sfile.exists()) { c2_player.setVolume(0, 0); } c2_player.setLooping(true); try { c2_player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) { c2_player.start(); } }); c2_player.setDataSource(video_path + "virtual.mp4"); c2_player.prepare(); } catch (Exception e) { XposedBridge.log("【VCAM】[c2player][" + c2_preview_Surfcae.toString() + "]" + e); } } if (c2_preview_Surfcae_1 != null) { XposedBridge.log("【VCAM c2_preview_Surfcae_1 非空】" ); if (c2_player_1 == null) { c2_player_1 = new MediaPlayer(); } else { c2_player_1.release(); c2_player_1 = new MediaPlayer(); } c2_player_1.setSurface(c2_preview_Surfcae_1); File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no-silent.jpg"); if (!sfile.exists()) { c2_player_1.setVolume(0, 0); } c2_player_1.setLooping(true); try { c2_player_1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) { c2_player_1.start(); } }); c2_player_1.setDataSource(video_path + "virtual.mp4"); c2_player_1.prepare(); } catch (Exception e) { XposedBridge.log("【VCAM】[c2player1]" + "[ " + c2_preview_Surfcae_1.toString() + "]" + e); } } XposedBridge.log("【VCAM】Camera2处理过程完全执行"); } private Surface create_virtual_surface() { if (need_recreate) { if (c2_virtual_surfaceTexture != null) { c2_virtual_surfaceTexture.release(); c2_virtual_surfaceTexture = null; } if (c2_virtual_surface != null) { c2_virtual_surface.release(); c2_virtual_surface = null; } c2_virtual_surfaceTexture = new SurfaceTexture(15); c2_virtual_surface = new Surface(c2_virtual_surfaceTexture); need_recreate = false; } else { if (c2_virtual_surface == null) { need_recreate = true; c2_virtual_surface = create_virtual_surface(); } } XposedBridge.log("【VCAM】【重建垃圾场】" + c2_virtual_surface.toString()); return c2_virtual_surface; } // 在HookMain类中添加 private void recreateVirtualSurface() { if (c2_virtual_surface != null) { XposedBridge.log("【VCAM】重建虚拟Surface: 1"); c2_virtual_surface.release(); c2_virtual_surface = null; } if (c2_virtual_surfaceTexture != null) { XposedBridge.log("【VCAM】重建虚拟Surface: 2"); c2_virtual_surfaceTexture.release(); c2_virtual_surfaceTexture = null; } c2_virtual_surfaceTexture = new SurfaceTexture(15); c2_virtual_surface = new Surface(c2_virtual_surfaceTexture); need_recreate = false; XposedBridge.log("【VCAM】重建虚拟Surface: " + c2_virtual_surface.toString()); } // 释放Camera2相关资源 private void releaseCamera2Resources() { if (c2_player != null) { c2_player.stop(); c2_player.release(); c2_player = null; } if (c2_player_1 != null) { c2_player_1.stop(); c2_player_1.release(); c2_player_1 = null; } if (c2_hw_decode_obj != null) { c2_hw_decode_obj.stopDecode(); c2_hw_decode_obj = null; } if (c2_hw_decode_obj_1 != null) { c2_hw_decode_obj_1.stopDecode(); c2_hw_decode_obj_1 = null; } c2_preview_Surfcae = null; c2_preview_Surfcae_1 = null; c2_reader_Surfcae = null; c2_reader_Surfcae_1 = null; } private void process_camera2_init(Class hooked_class) { XposedBridge.log("【VCAM】重启跟踪1" ); if(hooked_class==null){ XposedBridge.log("【VCAM】hooked_class 为null" ); } XposedHelpers.findAndHookMethod(hooked_class, "onOpened", CameraDevice.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】重启跟踪2"); // 重建虚拟Surface recreateVirtualSurface(); XposedBridge.log("【VCAM】重启跟踪3"); // 释放旧资源 releaseCamera2Resources(); XposedBridge.log("【VCAM】重启跟踪4"); XposedBridge.log("【VCAM】相机设备已打开"); XposedBridge.log("【VCAM】打开相机C2"); final CameraDevice cameraDevice = (CameraDevice) param.args[0]; File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists()) { if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "不存在替换视频\n" + toast_content.getPackageName() + "当前路径6:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee.toString()); } } return; } XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createCaptureSession", List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam paramd) throws Throwable { // 替换为虚拟Surface paramd.args[0] = Arrays.asList(c2_virtual_surface); XposedBridge.log("【VCAM】配置CaptureSession使用虚拟Surface"); // if (paramd.args[0] != null) { // XposedBridge.log("【VCAM】createCaptureSession创捷捕获,原始:" + paramd.args[0].toString() + "虚拟:" + c2_virtual_surface.toString()); // paramd.args[0] = Arrays.asList(c2_virtual_surface); // if (paramd.args[1] != null) { // process_camera2Session_callback((CameraCaptureSession.StateCallback) paramd.args[1]); // } // } } }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createCaptureSessionByOutputConfigurations", List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); if (param.args[0] != null) { outputConfiguration = new OutputConfiguration(c2_virtual_surface); param.args[0] = Arrays.asList(outputConfiguration); XposedBridge.log("【VCAM】执行了createCaptureSessionByOutputConfigurations-144777"); if (param.args[1] != null) { process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[1]); } } } }); } XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createConstrainedHighSpeedCaptureSession", List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); if (param.args[0] != null) { param.args[0] = Arrays.asList(c2_virtual_surface); XposedBridge.log("【VCAM】执行了 createConstrainedHighSpeedCaptureSession -5484987"); if (param.args[1] != null) { process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[1]); } } } }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createReprocessableCaptureSession", InputConfiguration.class, List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); if (param.args[1] != null) { param.args[1] = Arrays.asList(c2_virtual_surface); XposedBridge.log("【VCAM】执行了 createReprocessableCaptureSession "); if (param.args[2] != null) { process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[2]); } } } }); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createReprocessableCaptureSessionByConfigurations", InputConfiguration.class, List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); if (param.args[1] != null) { outputConfiguration = new OutputConfiguration(c2_virtual_surface); param.args[0] = Arrays.asList(outputConfiguration); XposedBridge.log("【VCAM】执行了 createReprocessableCaptureSessionByConfigurations"); if (param.args[2] != null) { process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[2]); } } } }); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createCaptureSession", SessionConfiguration.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); if (param.args[0] != null) { XposedBridge.log("【VCAM】执行了 createCaptureSession -5484987"); sessionConfiguration = (SessionConfiguration) param.args[0]; outputConfiguration = new OutputConfiguration(c2_virtual_surface); fake_sessionConfiguration = new SessionConfiguration(sessionConfiguration.getSessionType(), Arrays.asList(outputConfiguration), sessionConfiguration.getExecutor(), sessionConfiguration.getStateCallback()); param.args[0] = fake_sessionConfiguration; process_camera2Session_callback(sessionConfiguration.getStateCallback()); } } }); } } }); XposedHelpers.findAndHookMethod(hooked_class, "onError", CameraDevice.class, int.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】相机错误onerror:" + (int) param.args[1]); } }); XposedHelpers.findAndHookMethod(hooked_class, "onDisconnected", CameraDevice.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】相机断开onDisconnected :"); } }); } private void process_a_shot_jpeg(XC_MethodHook.MethodHookParam param, int index) { try { XposedBridge.log("【VCAM】第二个jpeg:" + param.args[index].toString()); } catch (Exception eee) { XposedBridge.log("【VCAM】" + eee); } Class callback = param.args[index].getClass(); } private void process_a_shot_YUV(XC_MethodHook.MethodHookParam param) { try { XposedBridge.log("【VCAM】发现拍照YUV:" + param.args[1].toString()); } catch (Exception eee) { XposedBridge.log("【VCAM】" + eee); } Class callback = param.args[1].getClass(); } private void process_callback(XC_MethodHook.MethodHookParam param) { Class preview_cb_class = param.args[0].getClass(); int need_stop = 0; File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg"); if (control_file.exists()) { need_stop = 1; } File file = new File(video_path + "virtual.mp4"); File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg"); need_to_show_toast = !toast_control.exists(); if (!file.exists()) { if (toast_content != null && need_to_show_toast) { try { Toast.makeText(toast_content, "不存在替换视频\n" + toast_content.getPackageName() + "当前路径7:" + video_path, Toast.LENGTH_SHORT).show(); } catch (Exception ee) { XposedBridge.log("【VCAM】[toast]" + ee); } } need_stop = 1; } int finalNeed_stop = need_stop; } private void process_camera2Session_callback(CameraCaptureSession.StateCallback callback_calss){ if (callback_calss == null){ return; } XposedBridge.log("【VCAM】process_camera2Session_callback :" + callback_calss.getClass().getName()); XposedHelpers.findAndHookMethod(callback_calss.getClass(), "onConfigureFailed", CameraCaptureSession.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】onConfigureFailed :" + param.args[0].toString()); } }); XposedHelpers.findAndHookMethod(callback_calss.getClass(), "onConfigured", CameraCaptureSession.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】onConfigured :" + param.args[0].toString()); } }); XposedHelpers.findAndHookMethod( callback_calss.getClass(), "onClosed", CameraCaptureSession.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("【VCAM】onClosed :"+ param.args[0].toString()); } }); } // 强制重启相机方法 private void forceRestartCamera(XC_LoadPackage.LoadPackageParam lpparam) { try { // 1. 关闭当前相机会话 if (c2_state_cb != null) { XposedBridge.log("【VCAM】断开相机链接 onDisconnected"); Method onDisconnected = c2_state_callback.getMethod("onDisconnected", CameraDevice.class); onDisconnected.invoke(c2_state_cb, (Object)null); } // 2. 重建虚拟Surface recreateVirtualSurface(); // 3. 模拟重新打开相机 XposedBridge.log("【VCAM】准备重启相机 openCamera"); XposedBridge.log("【VCAM】c2_state_callback " + c2_state_callback.getClass().getName().toString()); process_camera2_init(c2_state_callback); process_camera2_play(); XposedBridge.log("【VCAM】强制重启相机成功"); } catch (Exception e) { XposedBridge.log("【VCAM】强制重启相机失败: " + e.toString()); } } //以下代码来源:https://blog.csdn.net/jacke121/article/details/73888732 private Bitmap getBMP(String file) throws Throwable { return BitmapFactory.decodeFile(file); } private static byte[] rgb2YCbCr420(int[] pixels, int width, int height) { int len = width * height; // yuv格式数组大小,y亮度占len长度,u,v各占len/4长度。 byte[] yuv = new byte[len * 3 / 2]; int y, u, v; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int rgb = (pixels[i * width + j]) & 0x00FFFFFF; int r = rgb & 0xFF; int g = (rgb >> 8) & 0xFF; int b = (rgb >> 16) & 0xFF; // 套用公式 y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16; u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; y = y < 16 ? 16 : (Math.min(y, 255)); u = u < 0 ? 0 : (Math.min(u, 255)); v = v < 0 ? 0 : (Math.min(v, 255)); // 赋值 yuv[i * width + j] = (byte) y; yuv[len + (i >> 1) * width + (j & ~1)] = (byte) u; yuv[len + +(i >> 1) * width + (j & ~1) + 1] = (byte) v; } } return yuv; } private static byte[] getYUVByBitmap(Bitmap bitmap) { if (bitmap == null) { return null; } int width = bitmap.getWidth(); int height = bitmap.getHeight(); int size = width * height; int[] pixels = new int[size]; bitmap.getPixels(pixels, 0, width, 0, 0, width, height); return rgb2YCbCr420(pixels, width, height); } } forceRestartCamera 调用时发现 c2_state_callback为null

package com.chinaiatb.mrdoctor.utils; import android.Manifest; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.provider.MediaStore; import androidx.annotation.NonNull; import androidx.core.content.FileProvider; import android.provider.OpenableColumns; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.WindowManager; import android.webkit.MimeTypeMap; import android.widget.TextView; import android.widget.Toast; import com.blankj.utilcode.util.LogUtils; import com.chinaiatb.mrdoctor.R; import com.chinaiatb.mrdoctor.base.ui.widget.ActionSheetDialog; import com.chinaiatb.mrdoctor.base.utils.FileUtils; import com.chinaiatb.mrdoctor.base.utils.PermissionUtils; import com.chinaiatb.mrdoctor.base.utils.ToastUtil; import com.chinaiatb.mrdoctor.configs.GlobalConstant; import com.chinaiatb.mrdoctor.configs.ParamsConfigs; import com.chinaiatb.mrdoctor.ui.test.friend.view.SelectImageActivity; import com.chinaiatb.mrdoctor.ui.test.model.Image; import com.google.android.material.snackbar.Snackbar; import com.huawei.hms.utils.IOUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import static com.blankj.utilcode.util.StringUtils.getString; import static com.chinaiatb.mrdoctor.configs.IntentParameter.TAG; /** * description: * 2020/12/10. * auth:lihe */ public class AddPictureFileUtils { private ActionSheetDialog sheetDialog; private Activity mActivity; public AddPictureFileUtils(Activity activity, int singleOrMulti, boolean needCrop){ this.mActivity = activity; this.singleOrMulti = singleOrMulti; if(needCrop){ FILE_SAVEPATH = getSDPath(mActivity.getBaseContext()); // SELECT_FILE_SAVEPATH = getSDPath(mActivity.getBaseContext()); TEMP_DIR_PATH = ParamsConfigs.TEMP_SAVE_PATH_11; } } public AddPictureFileUtils(Activity activity, int singleOrMulti){ this.mActivity = activity; this.singleOrMulti = singleOrMulti; FILE_SAVEPATH = getSDPath(mActivity.getBaseContext()); } private final static String AUTHORITY_NAME = "com.chinaiatb.mrdoctor.fileprovider"; private String FILE_SAVEPATH = ParamsConfigs.PICTURE_SAVE_PATH; private String TEMP_DIR_PATH = ParamsConfigs.TEMP_SAVE_PATH; private String SELECT_FILE_SAVEPATH = ParamsConfigs.SELECT_FILE_SAVE_PATH; private String FileFlag; private Uri origUri;//原始uri private Uri cropUri;//剪裁后的uri private Uri selectFileUri; private File uploadPicFile;//上传照片 private String uploadPicPath;//上传照片路径 private File uploadSelectFile;//上传文件 private String uploadSelectFilePath;//上传文件路径 private List<String> selectedFileNames = new ArrayList<>(); private File uploadOriPicFile;//原始照片 private String uploadOriPicPath;//原始照片路径 private String fileType; private boolean needCutPicture;//标记是否需要剪裁 private int singleOrMulti = SINGLE_CHOOSE;//标记单选还是多选 public static final int SINGLE_CHOOSE = 1;//照片单选f public static final int MULTI_CHOOSE = 2;//照片多选 /** 请求相册 */ public static final int REQUEST_CODE_GETIMAGE_BYSDCARD = 0; /** 请求相机 */ public static final int REQUEST_CODE_GETIMAGE_BYCAMERA = 1; /** 请求裁剪 */ public static final int REQUEST_CODE_GETIMAGE_BYCROP = 2; /** 请求文件 */ public static final int REQUEST_CODE_GETFILE = 3; private final int CROP = 200; /** 请求存储权限 */ private static final int REQUEST_CODE_STORAGE_PERMISSION = 4; /** * 检查相机存储权限 */ public void checkPremission(boolean needCutPicture,String fileFlag) { FileFlag = fileFlag; this.needCutPicture = needCutPicture; CharSequence[] items = { getString(R.string.str_img_from_camera), getString(R.string.str_img_from_album) }; imageChooseItem(items); } /* * 获取SDK=30及以上的存储路径 * */ public static String getSDPath(Context context) { File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED);// 判断sd卡是否存在 if (sdCardExist) { // if (Build.VERSION.SDK_INT>=29){ // //Android10之后 // sdDir = context.getExternalFilesDir(null); // }else { // //sdDir = Environment.getExternalStorageDirectory();// 获取SD卡根目录 // sdDir = context.getFilesDir(); // } sdDir = context.getExternalFilesDir(null); } else { //sdDir = Environment.getRootDirectory();// 获取跟目录 sdDir= context.getFilesDir(); } return sdDir.toString(); } public void checkCameraPermissions(){ String[] permissions = new String[]{ // Manifest.permission.WRITE_EXTERNAL_STORAGE, // Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}; //这里的this不是上下文,是Activity对象! PermissionUtils.getInstance().chekPermissions(mActivity, permissions, permissionsCameraResult,1); // ToastUtil.showLongToastTop("用于扫码登录、扫码签名、患者问题答复时添加图片或视频信息"); } //创建监听权限的接口对象 PermissionUtils.IPermissionsResult permissionsCameraResult = new PermissionUtils.IPermissionsResult() { @Override public void passPermissons() { onSheetDialogClick(1); } @Override public void forbitPermissons() { ToastUtil.showShortToast("没有权限!"); } }; public void checkFilePermissions(){ String[] permissions = new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, }; //这里的this不是上下文,是Activity对象! PermissionUtils.getInstance().chekPermissions(mActivity, permissions, permissionsFileResult,1); } PermissionUtils.IPermissionsResult permissionsFileResult = new PermissionUtils.IPermissionsResult() { @Override public void passPermissons() { onSheetDialogClick(2); } @Override public void forbitPermissons() { ToastUtil.showShortToast("没有权限!"); } }; public void checkAlbumPermissions(){ String[] permissions = new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}; //这里的this不是上下文,是Activity对象! PermissionUtils.getInstance().chekPermissions(mActivity, permissions, permissionsAlbumResult,1); } //创建监听权限的接口对象 PermissionUtils.IPermissionsResult permissionsAlbumResult = new PermissionUtils.IPermissionsResult() { @Override public void passPermissons() { onSheetDialogClick(0); } @Override public void forbitPermissons() { ToastUtil.showShortToast("没有权限!"); } }; public void initDialog(){ if(null != sheetDialog){ return; } sheetDialog = new ActionSheetDialog(mActivity).builder(); sheetDialog.addSheetItem(getString(R.string.str_img_from_camera), null, new ActionSheetDialog.OnSheetItemClickListener() { @Override public void onClick(int which) { if(GlobalConstant.isAtRoomActivity){//还在视频过程中,不能拍照 ToastUtil.showShortToast("视频通话中,无法使用此功能"); }else{ checkCameraPermissions(); } } }); sheetDialog.addSheetItem(getString(R.string.str_img_from_album), null, new ActionSheetDialog.OnSheetItemClickListener() { @Override public void onClick(int which) { checkAlbumPermissions(); } }); //if (FileFlag=="1"){ if ("1".equals(FileFlag)){ sheetDialog.addSheetItem(getString(R.string.str_file_select), null, new ActionSheetDialog.OnSheetItemClickListener() { @Override public void onClick(int which) { checkFilePermissions(); } });} } /** * 相机选择和拍照 * @param items */ private void imageChooseItem(CharSequence[] items) { mActivity.runOnUiThread(new Runnable() { @Override public void run() { initDialog(); if (mActivity == null || mActivity.isDestroyed() || mActivity.isFinishing()) { return; } sheetDialog.show(); } }); } private void onSheetDialogClick(int position){ //判断是否挂载了SD卡 String storageState = Environment.getExternalStorageState(); if(storageState.equals(Environment.MEDIA_MOUNTED)){ File savedir = new File(FILE_SAVEPATH); File tempdir = new File(TEMP_DIR_PATH); if (!savedir.exists()) { savedir.mkdirs(); } if (!tempdir.exists()) { tempdir.mkdirs(); } } else{ ToastUtil.showShortToast("SD卡未挂载,不能操作照片"); return; } //输出裁剪的临时文件 String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date()); //照片命名 String origFileName = "byt_" + timeStamp + ".jpg"; String cropFileName = "byt_crop_" + timeStamp + ".jpg"; uploadOriPicPath = FILE_SAVEPATH + origFileName; uploadOriPicFile = new File(uploadOriPicPath); //裁剪头像的绝对路径 uploadPicPath = FILE_SAVEPATH + cropFileName; uploadPicFile = new File(uploadPicPath); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { origUri = FileProvider.getUriForFile(mActivity, AUTHORITY_NAME, uploadOriPicFile); }else{ origUri = Uri.fromFile(uploadOriPicFile); } cropUri = Uri.fromFile(uploadPicFile); //相册选图 if(position == 0) { if(singleOrMulti == SINGLE_CHOOSE){ startImagePick(); }else{ startActivity(); } } //手机拍照 else if(position == 1){ startActionCamera(); } //文件管理 else if(position == 2) { startActionFile(); } } private ArrayList<Image> mSelectImages = new ArrayList<>(); public static final int SELECT_IMAGE_REQUEST = 0x0011; private void startActivity() { Intent intent = new Intent(mActivity, SelectImageActivity.class); intent.putParcelableArrayListExtra("selected_images", mSelectImages); mActivity.startActivityForResult(intent, SELECT_IMAGE_REQUEST); } /** * 选择图片并直接裁剪 */ private void startImagePick() { // // 检查权限是否已授予 // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // if (!Environment.isExternalStorageManager()) { // // 如果没有权限,打开设置页面让用户授予权限 // Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); // mActivity.startActivityForResult(intent, REQUEST_CODE_STORAGE_PERMISSION); // return; // } // } // // 否则,执行正常的图片选择操作 Intent intent = new Intent(Intent.ACTION_PICK,null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); mActivity.startActivityForResult(Intent.createChooser(intent, "选择图片"),REQUEST_CODE_GETIMAGE_BYSDCARD); } /** * 相机拍照 */ private void startActionCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, selectFileUri/*this.getCameraTempFile()*/); // 添加这一句表示对目标应用临时授权该Uri所代表的文件 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } mActivity.startActivityForResult(intent, REQUEST_CODE_GETIMAGE_BYCAMERA); } private void startActionFile() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); intent.addCategory(Intent.CATEGORY_OPENABLE); // 添加可打开的文件分类 mActivity.startActivityForResult(Intent.createChooser(intent, "选择文件"), REQUEST_CODE_GETFILE); } /** * 拍照后裁剪 * @param data 原始图片 */ private void startActionCrop(Uri data, boolean fromCamera) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(data, "image/*"); if (fromCamera && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } intent.putExtra("return-data", false); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1);// 裁剪框比例 intent.putExtra("aspectY", 1); intent.putExtra("outputX", CROP); intent.putExtra("outputY", CROP); intent.putExtra("scale", true); intent.putExtra("scaleUpIfNeeded", true);// 去黑边 intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri); mActivity.startActivityForResult(intent, REQUEST_CODE_GETIMAGE_BYCROP); } private void startActionCrop2(File targetPhoto) { Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // intent.setAction(Intent.ACTION_OPEN_DOCUMENT); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.setDataAndType(getUriByFileForN(targetPhoto), "image/*"); }else{ intent.setDataAndType(Uri.fromFile(targetPhoto), "image/*"); } intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri); intent.putExtra("return-data", false); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1);// 裁剪框比例 intent.putExtra("aspectY", 1); intent.putExtra("outputX", CROP); intent.putExtra("outputY", CROP); intent.putExtra("scale", true); intent.putExtra("scaleUpIfNeeded", true);// 去黑边 mActivity.startActivityForResult(intent, REQUEST_CODE_GETIMAGE_BYCROP); } private void startActionCrop3(Uri sourceUri) { uploadPicFile = FileUtils.createImageFile(mActivity.getApplication(), true); Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Android N 及以上版本,确保裁剪图片时可以访问文件 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.setDataAndType(sourceUri, "image/*"); } // 设置裁剪输出文件的路径,针对不同版本需要做不同的处理 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 10 及以上版本,需要通过 FileProvider 获取 content:// URI intent.putExtra(MediaStore.EXTRA_OUTPUT, FileUtils.uri); // FileUtils.uri 是通过 FileProvider 提供的 URI } else { // 对于 Android 9 以下版本,使用 Uri.fromFile 来处理 file:// URI Uri imgCropUri = Uri.fromFile(uploadPicFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgCropUri); } //intent.putExtra("return-data", true); // 设置为 true 来返回裁剪数据 // 设置裁剪的相关参数 intent.putExtra("return-data", false); // 不返回裁剪数据,返回文件 URI intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); // 裁剪框的比例 intent.putExtra("aspectY", 1); intent.putExtra("outputX", CROP); // 裁剪后的宽度 intent.putExtra("outputY", CROP); // 裁剪后的高度 intent.putExtra("scale", true); // 允许缩放 intent.putExtra("scaleUpIfNeeded", true); // 去掉黑边 Log.d("yyyyy4", "needCutPicture="); mActivity.startActivityForResult(intent, REQUEST_CODE_GETIMAGE_BYCROP); } private Uri getImageContentUri(File imageFile){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { String filePath = imageFile.getAbsolutePath(); Cursor cursor = mActivity.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "=? ", new String[]{filePath}, null); try { if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { if (imageFile.exists()) { Uri tempUri = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { tempUri = FileProvider.getUriForFile(mActivity, AUTHORITY_NAME, imageFile); }else{ tempUri = Uri.fromFile(imageFile); } return tempUri; } } }catch(Exception e){ e.printStackTrace(); } finally { if (cursor != null) { cursor.close(); } } } else { return Uri.fromFile(imageFile); } return null; } /** * Android7.0根据文件获取uri */ private Uri getUriByFileForN( File imageFile) { String filePath = imageFile.getAbsolutePath(); Cursor cursor = mActivity.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "=? ", new String[]{filePath}, null); if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor .getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { Uri tempUri = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { tempUri = FileProvider.getUriForFile(mActivity, AUTHORITY_NAME, imageFile); }else{ tempUri = Uri.fromFile(imageFile); } return tempUri; } } /*************************************************************************/ /** * 选择相册图片时保存临时图片并获取图片临时路径 * @param data * @return */ private String getPhotoPathByUri(Intent data) { Uri contentUri = data.getData(); //copy file and send new file path String fileName = getFileName(contentUri); if (!TextUtils.isEmpty(fileName)) { File copyFile = new File(TEMP_DIR_PATH + File.separator + fileName); copy(contentUri, copyFile); return copyFile.getAbsolutePath(); } return null; } public String getFileName(Uri uri) { if (uri == null) return null; String fileName = null; String path = uri.getPath(); int cut = path.lastIndexOf('/'); if (cut != -1) { fileName = path.substring(cut + 1); } return fileName; } public void copy( Uri srcUri, File dstFile) { try { InputStream inputStream = mActivity.getContentResolver().openInputStream(srcUri); if (inputStream == null) return; OutputStream outputStream = new FileOutputStream(dstFile); IOUtils.copy(inputStream, outputStream); inputStream.close(); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } } private String saveSelectedFile(Uri srcUri) { String fileName = getFileName(srcUri); if (!TextUtils.isEmpty(fileName)) { File copyFile = new File(TEMP_DIR_PATH + File.separator + fileName); copy(srcUri, copyFile); return copyFile.getAbsolutePath(); } return null; } /*************************************************************************/ public String getUploadPicPath(){ return uploadPicPath; } public void setUploadPicPath(String uploadPicPath){ this.uploadPicPath = uploadPicPath; } public String getUploadOriPicPath(){ return uploadOriPicPath; } public void setUploadOriPicPath(String uploadOriPicPath){ this.uploadOriPicPath = uploadOriPicPath; } public String getUploadSelectFilePath(){ return uploadSelectFilePath; } public void setUploadSelectFilePath(String uploadSelectFilePath){ this.uploadSelectFilePath = uploadSelectFilePath; } public void dealOnActivityResult(int requestCode, int resultCode, Intent data,String AddFileFlag){ createFile(); Log.d("yyyyy0", "deal"); switch(requestCode) { case AddPictureFileUtils.REQUEST_CODE_GETIMAGE_BYCAMERA://拍照后的返回 if(!uploadOriPicFile.exists()) { //htc等手机可能存储在别的地方 Bitmap cameraBitMapData; if (data != null && data.hasExtra("data")) {//Intent带出 cameraBitMapData = data.getParcelableExtra("data"); } else { //存在系统路径 //获取系统拍照图片文件夹 Uri uri_DCIM = null; if (data.getData() != null) { uri_DCIM = data.getData(); } else { uri_DCIM = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } String DCIMPath = ""; //本次照片绝对路径 Cursor cr = mActivity.getContentResolver().query(uri_DCIM, new String[]{MediaStore.Images.Media.DATA}, null, null, MediaStore.Images.Media.DATE_MODIFIED + " desc");//降序排找最新的 if (cr.moveToFirst()) { DCIMPath = cr.getString(cr.getColumnIndex(MediaStore.Images.Media.DATA)); } cr.close(); cameraBitMapData = BitmapFactory.decodeFile(DCIMPath); } try { uploadOriPicFile.createNewFile(); //创建文件 FileOutputStream fos = new FileOutputStream(uploadOriPicFile); cameraBitMapData.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); } catch (IOException e) { LogUtils.w("=拍照图片保存发生异常!" + e); } } if(needCutPicture){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { startActionCrop3(getImageContentUri(uploadOriPicFile)); } catch (Exception e) { e.printStackTrace(); } } else { startActionCrop(getImageContentUri(uploadOriPicFile), true);//拍照后裁剪 } }else{ uplodTakePhotoFile(); } break; case AddPictureFileUtils.REQUEST_CODE_GETIMAGE_BYSDCARD://相册选择后的返回 Uri pickUri = data.getData(); Log.d("yyyyy", "pickUri: "+pickUri+"pickUri.getPath()"+pickUri.getPath()+"needCutPicture="+needCutPicture); if(pickUri != null && pickUri.getPath() != null){ if(needCutPicture){ Log.d("yyyyy1", "needCutPicture="+needCutPicture); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { Log.d("yyyyy2", "needCutPicture="+needCutPicture); //Uri fileUri = Uri.fromFile(new File(filePath)); Uri fileUri = FileProvider.getUriForFile(mActivity, "com.chinaiatb.mrdoctor.fileprovider", new File(getPhotoPathByUri(data))); //startActionCrop3(pickUri); startActionCrop3(fileUri); //startActionCrop2(new File(getPhotoPathByUri(data)));// 选图后裁剪 }catch (Exception e){ Log.d("yyyyy3", "needCutPicture="+needCutPicture); e.printStackTrace(); } }else{ startActionCrop2(new File(getPhotoPathByUri(data)));// 选图后裁剪 } }else{ uploadPhotoAlbumFile(data); } }else{ ToastUtil.showShortToast(getString(R.string.str_selected_pic_err)); } break; case AddPictureFileUtils.REQUEST_CODE_GETFILE: Uri pickFileUri = data.getData(); if (pickFileUri != null && pickFileUri.getPath() != null) { uploadSelectFilePath = getFilePathFromUri(mActivity.getApplicationContext(), pickFileUri); Log.d(TAG, "dealOnActivityResult: "+uploadSelectFilePath); fileType = getFileMimeType(mActivity.getApplicationContext(), pickFileUri); uploadSelectFile = new File(uploadSelectFilePath); String fileName = uploadSelectFile.getName(); selectedFileNames.add(fileName); uploadSelectFile(); if (uploadSelectFile.exists()) { Log.d(TAG, "文件保存成功"); // 执行其他操作 } else { Log.d(TAG, "文件保存失败"); // 处理文件保存失败的情况 } }break; case AddPictureFileUtils.SELECT_IMAGE_REQUEST://选择多张照片 if (data != null) { ArrayList<Image> selectImages = data.getParcelableArrayListExtra(SelectImageActivity.EXTRA_RESULT); uploadPhotoFiles(selectImages); } break; case REQUEST_CODE_GETIMAGE_BYCROP: // 单张剪裁后上传 Log.d("yyyyy11", "deal"); if (!uploadPicFile.exists()) { ToastUtil.showShortToast(getString(R.string.str_crop_pic_err)); } else { uploadAfterCutPhoto(); } break; } } public String getFileMimeType(Context context, Uri fileUri) { ContentResolver contentResolver = context.getContentResolver(); String fileMimeType = contentResolver.getType(fileUri); if (fileMimeType == null) { // 如果获取的MIME类型为空,尝试使用文件扩展名来推断MIME类型 String fileExtension = getFileExtension(context, fileUri); fileMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension); } return fileMimeType; } private String getFileExtension(Context context, Uri fileUri) { ContentResolver contentResolver = context.getContentResolver(); MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); // 从文件Uri获取文件名 String fileName = null; Cursor cursor = contentResolver.query(fileUri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { int displayNameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (displayNameIndex != -1) { fileName = cursor.getString(displayNameIndex); } } // 从文件名中获取扩展名 String fileExtension = null; if (fileName != null) { int dotIndex = fileName.lastIndexOf("."); if (dotIndex != -1 && dotIndex < fileName.length() - 1) { fileExtension = fileName.substring(dotIndex + 1).toLowerCase(); } } return fileExtension; } private String getFilePathFromUri(Context context, Uri uri) { String filePath = null; if (uri.getScheme().equals("content")) { ContentResolver contentResolver = context.getContentResolver(); Cursor cursor = contentResolver.query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA); filePath = cursor.getString(columnIndex); cursor.close(); } } else if (uri.getScheme().equals("file")) { filePath = uri.getPath(); } return filePath; } /** * 文件为空时创建文件 */ public void createFile(){ if(null == uploadPicFile){ uploadPicFile = new File(uploadPicPath); } if(null == uploadOriPicFile){ uploadOriPicFile = new File(uploadOriPicPath); } } /** * 拍照后上传原始图片 */ public void uplodTakePhotoFile(){ if(null != addPictureFileListener){ addPictureFileListener.uploadFile(uploadOriPicFile, FileUtils.getFileFormat(uploadOriPicPath)); Log.d(TAG, "uplodTakePhotoFile: "+uploadOriPicPath); } } /** * 从相册直接选择的单张照片 * @param data */ public void uploadPhotoAlbumFile(Intent data){ if(null != addPictureFileListener){ addPictureFileListener.uploadFile(new File(getPhotoPathByUri(data)), FileUtils.getFileFormat(uploadPicPath)); Log.d(TAG, "uploadPhotoAlbumFile: "+FileUtils.getFileFormat(uploadPicPath)); } } public void uploadSelectFile(){ if (selectedFileNames != null) { // 打印selectedFileNames数组 for (String fileName : selectedFileNames) { Log.d("Array", fileName); } } else { Log.d("Array", "selectedFileNames is null"); } if(null != addPictureFileListener){ addPictureFileListener.uploadSelectFileList(selectedFileNames); addPictureFileListener.uploadSelectFile(uploadSelectFile, FileUtils.getFileFormat(uploadSelectFilePath)); } } /** * 一组照片的上传 * @param selectImages */ public void uploadPhotoFiles(ArrayList<Image> selectImages){ if(null != addPictureFileListener){ addPictureFileListener.uploadFileList(selectImages); } } /** * 剪裁后的照片上传 */ public void uploadAfterCutPhoto(){ if(null != addPictureFileListener){ addPictureFileListener.uploadFile(uploadPicFile, FileUtils.getFileFormat(uploadPicPath)); } } private AddPictureFileListener addPictureFileListener; public interface AddPictureFileListener{ void uploadFile(File file, String suffix); void uploadSelectFile(File file, String suffix); void uploadFileList(ArrayList<Image> selectImages); void uploadSelectFileList(List<String> selectedFileNames); } public void setAddPictureFileListener(AddPictureFileListener addPictureFileListener){ this.addPictureFileListener = addPictureFileListener; } } 这个代码中拍照和从相册选择图片上传 关于申请权限问题是一起的吗还是分开的。

zip
资源下载链接为: https://pan.quark.cn/s/9648a1f24758 MySQL SSH隧道连接是一种安全的远程访问MySQL数据库的方式,尤其适用于无法直接远程访问MySQL服务器的场景。通过SSH协议建立安全通道,将本地网络连接转发至远程服务器,从而实现本地对远程数据库的访问,就如同访问本地MySQL服务一样。 如果之前为root用户分配了远程访问权限,出于安全考虑,可以按照以下步骤收回权限: 登录MySQL服务器:mysql -uroot -p。 查看当前用户的远程访问权限:select user, host from mysql.user;。 删除不必要的远程访问权限(例如限制root用户从所有主机访问):delete from mysql.user where user='root' and host='%';。 刷新权限以应用更改:flush privileges;。 再次查询,确认root用户已无法从任何远程主机登录。 接下来,需要建立SSH隧道。通常使用SSH客户端(如OpenSSH)连接到远程服务器,并通过RSA密钥对进行身份验证,这是一种更安全的认证方式。如果本地尚未生成RSA密钥对,需要先生成密钥对,并将公钥(id_rsa.pub)添加到远程服务器的~/.ssh/authorized_keys文件中。建立SSH隧道的命令如下(需替换为实际的IP地址和端口号): 参数含义如下: -N:仅建立隧道,不执行远程命令。 -C:启用压缩(可选)。 -P:使用非特权端口。 -f:建立隧道后在后台运行。 [email protected]:SSH用户名和目标服务器IP。 -L 3388:127.0.0.1:3306:将本地端口3388映射到远程服务器的MySQL端口3306。 之后,可以使用本地MySQL客户端工具(如MySQL Workben

最新推荐

recommend-type

基于 DrissionPage 的淘宝商品秒杀 Python 脚本

资源下载链接为: https://pan.quark.cn/s/9960c5318670 基于 DrissionPage 的淘宝商品秒杀 Python 脚本(最新、最全版本!打开链接下载即可用!)
recommend-type

(完整版)版铁路公开招聘笔试题库必考点含答案.pdf

(完整版)版铁路公开招聘笔试题库必考点含答案.pdf
recommend-type

会计电算化教材配套资源ppt课件(完整版).zip

会计电算化教材配套资源ppt课件(完整版).zip
recommend-type

MySQL通过SSH隧道连接的详细操作步骤

资源下载链接为: https://pan.quark.cn/s/9648a1f24758 MySQL SSH隧道连接是一种安全的远程访问MySQL数据库的方式,尤其适用于无法直接远程访问MySQL服务器的场景。通过SSH协议建立安全通道,将本地网络连接转发至远程服务器,从而实现本地对远程数据库的访问,就如同访问本地MySQL服务一样。 如果之前为root用户分配了远程访问权限,出于安全考虑,可以按照以下步骤收回权限: 登录MySQL服务器:mysql -uroot -p。 查看当前用户的远程访问权限:select user, host from mysql.user;。 删除不必要的远程访问权限(例如限制root用户从所有主机访问):delete from mysql.user where user='root' and host='%';。 刷新权限以应用更改:flush privileges;。 再次查询,确认root用户已无法从任何远程主机登录。 接下来,需要建立SSH隧道。通常使用SSH客户端(如OpenSSH)连接到远程服务器,并通过RSA密钥对进行身份验证,这是一种更安全的认证方式。如果本地尚未生成RSA密钥对,需要先生成密钥对,并将公钥(id_rsa.pub)添加到远程服务器的~/.ssh/authorized_keys文件中。建立SSH隧道的命令如下(需替换为实际的IP地址和端口号): 参数含义如下: -N:仅建立隧道,不执行远程命令。 -C:启用压缩(可选)。 -P:使用非特权端口。 -f:建立隧道后在后台运行。 [email protected]:SSH用户名和目标服务器IP。 -L 3388:127.0.0.1:3306:将本地端口3388映射到远程服务器的MySQL端口3306。 之后,可以使用本地MySQL客户端工具(如MySQL Workben
recommend-type

生态学基于Python的生态熵指数(EEI)计算与统计分析:生态环境评估与敏感性分析系统设计以下要点:

内容概要:本文档提供了一个Python脚本,用于计算生态熵指数(EEI)并进行统计分析。首先,通过pip安装必要的库,如SALib、xgboost和shap。接着,定义了样本数据集,包括年份、地点、盐度、生物多样性和能量流等属性。然后,对数据进行归一化处理,并基于生物多样性值计算Shannon熵。随后,根据公式计算EEI,并使用Sobol敏感性分析评估各因素对EEI的影响。此外,利用XGBoost模型结合SHAP解释器分析特征重要性。还应用CUSUM算法检测阈值变化,并进行了Shapiro-Wilk正态性检验。最后,将结果保存为CSV文件,并生成EEI趋势图和散点图以可视化分析结果。; 适合人群:具备一定编程基础,特别是熟悉Python和数据分析的研究人员或工程师。; 使用场景及目标:①通过Sobol敏感性分析了解各因素对EEI的影响程度;②利用XGBoost和SHAP分析特征重要性,辅助决策;③通过CUSUM算法和正态性检验识别异常变化和数据分布特征。; 阅读建议:此资源涉及多个数据分析技术和统计方法,建议读者在学习过程中结合具体代码实现,逐步理解每个步骤的目的和实现细节,并参考相关文献加深理解。
recommend-type

2022版微信自定义密码锁定程序保护隐私

标题《微信锁定程序2022,自定义密码锁》和描述“微信锁定程序2022,自定义密码锁,打开微信需要填写自己设定的密码,才可以查看微信信息和回复信息操作”提及了一个应用程序,该程序为微信用户提供了额外的安全层。以下是对该程序相关的知识点的详细说明: 1. 微信应用程序安全需求 微信作为一种广泛使用的即时通讯工具,其通讯内容涉及大量私人信息,因此用户对其隐私和安全性的需求日益增长。在这样的背景下,出现了第三方应用程序或工具,旨在增强微信的安全性和隐私性,例如我们讨论的“微信锁定程序2022”。 2. “自定义密码锁”功能 “自定义密码锁”是一项特定功能,允许用户通过设定个人密码来增强微信应用程序的安全性。这项功能要求用户在打开微信或尝试查看、回复微信信息时,必须先输入他们设置的密码。这样,即便手机丢失或被盗,未经授权的用户也无法轻易访问微信中的个人信息。 3. 实现自定义密码锁的技术手段 为了实现这种类型的锁定功能,开发人员可能会使用多种技术手段,包括但不限于: - 加密技术:对微信的数据进行加密,确保即使数据被截获,也无法在没有密钥的情况下读取。 - 应用程序层锁定:在软件层面添加一层权限管理,只允许通过验证的用户使用应用程序。 - 操作系统集成:与手机操作系统的安全功能进行集成,利用手机的生物识别技术或复杂的密码保护微信。 - 远程锁定与擦除:提供远程锁定或擦除微信数据的功能,以应对手机丢失或被盗的情况。 4. 微信锁定程序2022的潜在优势 - 增强隐私保护:防止他人未经授权访问微信账户中的对话和媒体文件。 - 防止数据泄露:在手机丢失或被盗的情况下,减少敏感信息泄露的风险。 - 保护未成年人:父母可以为孩子设定密码,控制孩子的微信使用。 - 为商业用途提供安全保障:在商务场合,微信锁定程序可以防止商业机密的泄露。 5. 使用微信锁定程序2022时需注意事项 - 正确的密码管理:用户需要记住设置的密码,并确保密码足够复杂,不易被破解。 - 避免频繁锁定:过于频繁地锁定和解锁可能会降低使用微信的便捷性。 - 兼容性和更新:确保微信锁定程序与当前使用的微信版本兼容,并定期更新以应对安全漏洞。 - 第三方应用风险:使用第三方应用程序可能带来安全风险,用户应从可信来源下载程序并了解其隐私政策。 6. 结语 微信锁定程序2022是一个创新的应用,它提供了附加的安全性措施来保护用户的微信账户。尽管在实施中可能会面临一定的挑战,但它为那些对隐私和安全有更高要求的用户提供了可行的解决方案。在应用此类程序时,用户应谨慎行事,确保其对应用程序的安全性和兼容性有所了解,并采取适当措施保护自己的安全密码。
recommend-type

【自动化脚本提速】:掌握序列生成的5种高效技巧

# 摘要 本文系统地阐述了自动化脚本提速的方法,重点介绍了序列生成的基础理论及其在脚本中的应用。通过探讨不同序列生成方法和高效技巧,本文旨在提高编程效率,优化自动化流程。同时,文中还涉及了高级技术,如嵌套循环、列表推导式和并行处理,这些技术不仅增加了序列生成的复杂性,同时也显著提升了效率。最后,本文通过综合案例分析,展示了一系列序列生成技巧的实际应用,并提出了优化建议和未来研究方向。 #
recommend-type

卷积神经网络中的分层!

<think>我们正在处理一个关于卷积神经网络(CNN)层级结构的问题。用户希望了解CNN的层级结构及其功能。根据提供的引用内容,我们可以整理出以下信息: 1. 引用[1]和[2]指出,一个完整的卷积神经网络通常包括以下层级: - 数据输入层(Input layer) - 卷积计算层(CONV layer) - ReLU激励层(ReLU layer) - 池化层(Pooling layer) - 全连接层(FC layer) - (可能还有)Batch Normalization层 2. 引用[2]详细说明了各层的作用: - 数据输入层:对原始图像
recommend-type

MXNet预训练模型介绍:arcface_r100_v1与retinaface-R50

根据提供的文件信息,我们可以从中提取出关于MXNet深度学习框架、人脸识别技术以及具体预训练模型的知识点。下面将详细说明这些内容。 ### MXNet 深度学习框架 MXNet是一个开源的深度学习框架,由Apache软件基金会支持,它在设计上旨在支持高效、灵活地进行大规模的深度学习。MXNet支持多种编程语言,并且可以部署在不同的设备上,从个人电脑到云服务器集群。它提供高效的多GPU和分布式计算支持,并且具备自动微分机制,允许开发者以声明性的方式表达神经网络模型的定义,并高效地进行训练和推理。 MXNet的一些关键特性包括: 1. **多语言API支持**:MXNet支持Python、Scala、Julia、C++等语言,方便不同背景的开发者使用。 2. **灵活的计算图**:MXNet拥有动态计算图(imperative programming)和静态计算图(symbolic programming)两种编程模型,可以满足不同类型的深度学习任务。 3. **高效的性能**:MXNet优化了底层计算,支持GPU加速,并且在多GPU环境下也进行了性能优化。 4. **自动并行计算**:MXNet可以自动将计算任务分配到CPU和GPU,无需开发者手动介入。 5. **扩展性**:MXNet社区活跃,提供了大量的预训练模型和辅助工具,方便研究人员和开发者在现有工作基础上进行扩展和创新。 ### 人脸识别技术 人脸识别技术是一种基于人的脸部特征信息进行身份识别的生物识别技术,广泛应用于安防、监控、支付验证等领域。该技术通常分为人脸检测(Face Detection)、特征提取(Feature Extraction)和特征匹配(Feature Matching)三个步骤。 1. **人脸检测**:定位出图像中人脸的位置,通常通过深度学习模型实现,如R-CNN、YOLO或SSD等。 2. **特征提取**:从检测到的人脸区域中提取关键的特征信息,这是识别和比较不同人脸的关键步骤。 3. **特征匹配**:将提取的特征与数据库中已有的人脸特征进行比较,得出最相似的人脸特征,从而完成身份验证。 ### 预训练模型 预训练模型是在大量数据上预先训练好的深度学习模型,可以通过迁移学习的方式应用到新的任务上。预训练模型的优点在于可以缩短训练时间,并且在标注数据较少的新任务上也能获得较好的性能。 #### arcface_r100_v1 arcface_r100_v1是一个使用ArcFace损失函数训练的人脸识别模型,基于ResNet-100架构。ArcFace是一种流行的深度学习人脸识别方法,它在损失函数层面上增强类间的区分度。在ArcFace中,通过引入角度余弦的特征分离度,改善了传统的Softmax损失函数,让学习到的人脸特征更加具有鉴别力。 ArcFace的模型文件包括: - model-0000.params: 这是模型权重参数文件。 - model-symbol.json: 这是包含网络结构定义的JSON文件。 #### retinaface-R50 retinaface-R50是基于ResNet-50架构的人脸检测模型,使用RetinaFace框架训练而成。RetinaFace是为了解决传统人脸检测模型在面对小尺寸、遮挡、模糊等复杂情况时识别准确度不高的问题而设计的。它采用一种基于多尺度的金字塔网络结构,能有效处理不同尺度的人脸,并且在特征提取时采用了一种高效的特征融合策略。 Retinaface-R50的模型文件包括: - R50-0000.params: 这是模型权重参数文件。 - R50-symbol.json: 这是包含网络结构定义的JSON文件。 ### 总结 从给定的文件信息中,我们可以看出这些预训练模型是基于MXNet深度学习框架开发的,具有专门针对人脸识别任务的优化。ArcFace模型通过增强特征的区分度,而Retinaface模型通过多尺度处理和高效的特征融合,都展示了在人脸检测和识别方面的先进技术。开发者可以利用这些预训练模型,结合MXNet提供的高级API,快速构建并部署自己的人脸识别解决方案。
recommend-type

【文本处理黑科技】:Shell脚本中序列和数组的高级应用

# 摘要 本文系统地介绍了Shell脚本中序列与数组的基础知识、操作技巧以及在实际应用中的优化方法。第一章和第二章分别对序列和数组的基础知识进行了阐述,重点讲解了序列的生成、操作、文本处理和数组的基本操作与高级特性。第三章探讨了数组在数据处理和内存管理中的性能优势。第四章结合实际案例,展示了序列与数组在日志文件分析、报告生成和配置文件管理