diff --git a/src/main/java/net/micode/notes/tool/AIService.java b/src/main/java/net/micode/notes/tool/AIService.java new file mode 100644 index 0000000..c2a0e93 --- /dev/null +++ b/src/main/java/net/micode/notes/tool/AIService.java @@ -0,0 +1,409 @@ +package net.micode.notes.tool; + +import android.content.Context; +import android.graphics.Bitmap; +import android.util.Base64; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +/** + * AIService - AI服务类 + *

+ * 用于处理与AI相关的服务调用,如豆包API + *

+ */ +public class AIService { + private static final String TAG = "AIService"; + private static final String DOUBAO_API_URL = "https://ark.cn-beijing.volces.com/api/v3/responses"; + private static final String API_KEY = "ee5fb4c7-ea14-4481-ac23-4b0e82907850"; + private static final String SECRET_ACCESS_KEY = ""; + + /** + * 提取图片内容 + * @param bitmap 图片bitmap + * @param callback 回调接口 + */ + public static void extractImageContent(final Bitmap bitmap, final ExtractImageContentCallback callback) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Log.d(TAG, "Starting image content extraction..."); + + // 检查bitmap + if (bitmap == null) { + Log.e(TAG, "Bitmap is null"); + callback.onFailure("Bitmap is null"); + return; + } + + Log.d(TAG, "Bitmap width: " + bitmap.getWidth() + ", height: " + bitmap.getHeight()); + + // 将bitmap转换为Base64 + Log.d(TAG, "Converting bitmap to base64..."); + String base64Image = bitmapToBase64(bitmap); + if (base64Image == null) { + Log.e(TAG, "Failed to convert bitmap to base64"); + callback.onFailure("Failed to convert bitmap to base64"); + return; + } + Log.d(TAG, "Base64 conversion successful, length: " + base64Image.length()); + + // 构建请求体 + Log.d(TAG, "Building request body..."); + JSONObject requestBody = new JSONObject(); + requestBody.put("model", "ep-20260127214554-frsrr"); // 新的推理接入点ID + + // 创建input数组 + org.json.JSONArray input = new org.json.JSONArray(); + + // 创建user input + JSONObject userInput = new JSONObject(); + userInput.put("role", "user"); + + // 创建content数组 + org.json.JSONArray contentArray = new org.json.JSONArray(); + + // 添加图片部分 + JSONObject imageContent = new JSONObject(); + imageContent.put("type", "input_image"); + imageContent.put("image_url", "data:image/jpeg;base64," + base64Image); + contentArray.put(imageContent); + + // 添加文本部分 + JSONObject textContent = new JSONObject(); + textContent.put("type", "input_text"); + textContent.put("text", "请提取这张图片中的所有文字和结构化数据,包括表格、列表等信息,清晰准确地格式化提取的内容。"); + contentArray.put(textContent); + + userInput.put("content", contentArray); + input.put(userInput); + + requestBody.put("input", input); + + // 发送请求 + String requestBodyString = requestBody.toString(); + Log.d(TAG, "Request body length: " + requestBodyString.length()); + Log.d(TAG, "Request body (first 1000 chars): " + (requestBodyString.length() > 1000 ? requestBodyString.substring(0, 1000) + "..." : requestBodyString)); + + Log.d(TAG, "Sending POST request to: " + DOUBAO_API_URL); + String response = sendPostRequest(DOUBAO_API_URL, requestBodyString); + + if (response == null) { + Log.e(TAG, "Failed to get response from Doubao API"); + callback.onFailure("Failed to get response from Doubao API"); + return; + } + + Log.d(TAG, "Got response from Doubao API, length: " + response.length()); + Log.d(TAG, "Response content: " + response); + + // 解析响应 + Log.d(TAG, "Parsing response..."); + JSONObject responseJson = new JSONObject(response); + + // 检查响应格式 + if (responseJson.has("output")) { + Log.d(TAG, "Response has output field"); + try { + // 尝试作为数组处理(新格式) + org.json.JSONArray outputArray = responseJson.getJSONArray("output"); + Log.d(TAG, "Output is an array, length: " + outputArray.length()); + + // 遍历数组找到包含文本的message + String extractedText = ""; + for (int i = 0; i < outputArray.length(); i++) { + JSONObject item = outputArray.getJSONObject(i); + Log.d(TAG, "Output item " + i + ": " + item.toString()); + + // 检查是否是message类型 + if (item.has("type") && "message".equals(item.getString("type"))) { + Log.d(TAG, "Found message item"); + if (item.has("content")) { + org.json.JSONArray messageContentArray = item.getJSONArray("content"); + for (int j = 0; j < messageContentArray.length(); j++) { + JSONObject contentItem = messageContentArray.getJSONObject(j); + if (contentItem.has("type") && "output_text".equals(contentItem.getString("type"))) { + extractedText = contentItem.getString("text"); + Log.d(TAG, "Got text from response: " + extractedText); + callback.onSuccess(extractedText); + return; + } + } + } + } + // 检查是否有role字段为assistant + else if (item.has("role") && "assistant".equals(item.getString("role"))) { + Log.d(TAG, "Found assistant item"); + if (item.has("content")) { + org.json.JSONArray assistantContentArray = item.getJSONArray("content"); + for (int j = 0; j < assistantContentArray.length(); j++) { + JSONObject contentItem = assistantContentArray.getJSONObject(j); + if (contentItem.has("type") && "output_text".equals(contentItem.getString("type"))) { + extractedText = contentItem.getString("text"); + Log.d(TAG, "Got text from assistant response: " + extractedText); + callback.onSuccess(extractedText); + return; + } + } + } + } + } + + // 如果没有找到文本,尝试其他方式 + if (extractedText.isEmpty()) { + Log.e(TAG, "No text found in output array"); + callback.onFailure("No text found in output array"); + } + } catch (JSONException e) { + // 如果不是数组,尝试作为对象处理(旧格式) + Log.d(TAG, "Output is not an array, trying as object: " + e.getMessage()); + try { + JSONObject outputObj = responseJson.getJSONObject("output"); + if (outputObj.has("text")) { + String content = outputObj.getString("text"); + Log.d(TAG, "Got text from response object: " + content); + callback.onSuccess(content); + } else if (outputObj.has("content")) { + String content = outputObj.getString("content"); + Log.d(TAG, "Got content from response object: " + content); + callback.onSuccess(content); + } else { + Log.e(TAG, "No text or content in response object: " + outputObj.toString()); + callback.onFailure("No text or content in response"); + } + } catch (JSONException ex) { + Log.e(TAG, "Error parsing output: " + ex.getMessage()); + callback.onFailure("Error parsing output: " + ex.getMessage()); + } + } + } else if (responseJson.has("choices")) { + // 兼容旧格式 + Log.d(TAG, "Response has choices field"); + org.json.JSONArray choices = responseJson.getJSONArray("choices"); + if (choices.length() > 0) { + JSONObject choice = choices.getJSONObject(0); + if (choice.has("message")) { + JSONObject message = choice.getJSONObject("message"); + if (message.has("content")) { + String content = message.getString("content"); + Log.d(TAG, "Got content from choices: " + content); + callback.onSuccess(content); + } else { + Log.e(TAG, "No content in message: " + message.toString()); + callback.onFailure("No content in message"); + } + } else { + Log.e(TAG, "No message in choice: " + choice.toString()); + callback.onFailure("No message in choice"); + } + } else { + Log.e(TAG, "No choices in response"); + callback.onFailure("No choices in response"); + } + } else if (responseJson.has("error")) { + // 处理错误响应 + Log.e(TAG, "API returned error: " + responseJson.toString()); + JSONObject error = responseJson.getJSONObject("error"); + String errorMessage = error.getString("message"); + callback.onFailure("API error: " + errorMessage); + } else { + Log.e(TAG, "Unexpected response format: " + responseJson.toString()); + callback.onFailure("Unexpected response format: " + responseJson.toString()); + } + + } catch (JSONException e) { + Log.e(TAG, "JSONException: " + e.getMessage()); + e.printStackTrace(); + callback.onFailure("JSON error: " + e.getMessage()); + } catch (Exception e) { + Log.e(TAG, "Exception: " + e.getMessage()); + e.printStackTrace(); + callback.onFailure("Error: " + e.getMessage()); + } + } + }).start(); + } + + /** + * 将Bitmap转换为Base64字符串 + * @param bitmap 图片bitmap + * @return Base64字符串 + */ + private static String bitmapToBase64(Bitmap bitmap) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream); + byte[] byteArray = byteArrayOutputStream.toByteArray(); + try { + byteArrayOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return Base64.encodeToString(byteArray, Base64.NO_WRAP); + } + + /** + * 发送POST请求 + * @param urlString URL字符串 + * @param requestBody 请求体 + * @return 响应字符串 + */ + private static String sendPostRequest(String urlString, String requestBody) { + try { + Log.d(TAG, "Sending POST request to: " + urlString); + Log.d(TAG, "Request body length: " + requestBody.length()); + Log.d(TAG, "Request body (first 500 chars): " + (requestBody.length() > 500 ? requestBody.substring(0, 500) + "..." : requestBody)); + + URL url = new URL(urlString); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + // 设置请求头 + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("Authorization", "Bearer " + API_KEY); + connection.setRequestProperty("X-TT-LOGID", System.currentTimeMillis() + ""); + connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"); + connection.setDoOutput(true); + connection.setConnectTimeout(30000); // 设置连接超时为30秒 + connection.setReadTimeout(30000); // 设置读取超时为30秒 + + // 写入请求体 + Log.d(TAG, "Writing request body..."); + OutputStream outputStream = connection.getOutputStream(); + outputStream.write(requestBody.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + outputStream.close(); + Log.d(TAG, "Request body written successfully"); + + // 读取响应 + Log.d(TAG, "Reading response..."); + int responseCode = connection.getResponseCode(); + Log.d(TAG, "HTTP response code: " + responseCode); + + // 读取所有响应头 + Log.d(TAG, "Response headers:"); + java.util.Map> headers = connection.getHeaderFields(); + for (String key : headers.keySet()) { + if (key != null) { + Log.d(TAG, key + ": " + headers.get(key)); + } + } + + if (responseCode == HttpURLConnection.HTTP_OK) { + Log.d(TAG, "HTTP OK, reading response body..."); + InputStream inputStream = connection.getInputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + ByteArrayOutputStream responseStream = new ByteArrayOutputStream(); + while ((bytesRead = inputStream.read(buffer)) != -1) { + responseStream.write(buffer, 0, bytesRead); + } + String responseString = responseStream.toString(StandardCharsets.UTF_8.name()); + responseStream.close(); + inputStream.close(); + connection.disconnect(); + Log.d(TAG, "API response length: " + responseString.length()); + Log.d(TAG, "API response (first 500 chars): " + (responseString.length() > 500 ? responseString.substring(0, 500) + "..." : responseString)); + return responseString; + } else { + // 读取错误响应 + Log.e(TAG, "HTTP error, reading error response..."); + InputStream errorStream = connection.getErrorStream(); + if (errorStream != null) { + byte[] buffer = new byte[1024]; + int bytesRead; + ByteArrayOutputStream errorResponseStream = new ByteArrayOutputStream(); + while ((bytesRead = errorStream.read(buffer)) != -1) { + errorResponseStream.write(buffer, 0, bytesRead); + } + String errorResponse = errorResponseStream.toString(StandardCharsets.UTF_8.name()); + errorResponseStream.close(); + errorStream.close(); + Log.e(TAG, "HTTP error: " + responseCode + ", Error response: " + errorResponse); + } else { + Log.e(TAG, "HTTP error code: " + responseCode + ", No error stream available"); + } + connection.disconnect(); + return null; + } + } catch (java.net.SocketTimeoutException e) { + Log.e(TAG, "Socket timeout error: " + e.getMessage()); + e.printStackTrace(); + return null; + } catch (java.net.ConnectException e) { + Log.e(TAG, "Connection error: " + e.getMessage()); + e.printStackTrace(); + return null; + } catch (java.io.IOException e) { + Log.e(TAG, "IO error: " + e.getMessage()); + e.printStackTrace(); + return null; + } catch (Exception e) { + Log.e(TAG, "Error sending POST request: " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + /** + * 提取图片内容回调接口 + */ + public interface ExtractImageContentCallback { + void onSuccess(String extractedContent); + void onFailure(String errorMessage); + } + + /** + * 测试API连接 + */ + public static void testApiConnection(final ExtractImageContentCallback callback) { + new Thread(new Runnable() { + @Override + public void run() { + try { + // 构建测试请求体 + JSONObject requestBody = new JSONObject(); + requestBody.put("model", "ep-20260127214554-frsrr"); + + org.json.JSONArray input = new org.json.JSONArray(); + JSONObject userInput = new JSONObject(); + userInput.put("role", "user"); + + org.json.JSONArray contentArray = new org.json.JSONArray(); + JSONObject textContent = new JSONObject(); + textContent.put("type", "input_text"); + textContent.put("text", "Hello, test connection"); + contentArray.put(textContent); + + userInput.put("content", contentArray); + input.put(userInput); + + requestBody.put("input", input); + + Log.d(TAG, "Testing API connection..."); + String response = sendPostRequest(DOUBAO_API_URL, requestBody.toString()); + + if (response != null) { + Log.d(TAG, "API connection test successful: " + response); + callback.onSuccess("API connection test successful"); + } else { + Log.e(TAG, "API connection test failed"); + callback.onFailure("API connection test failed"); + } + } catch (Exception e) { + Log.e(TAG, "Error testing API connection: " + e.getMessage()); + callback.onFailure("Error testing API connection: " + e.getMessage()); + } + } + }).start(); + } +} diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 013726d..bf69f19 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -119,6 +119,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, public ImageButton ibRedo; // 重做按钮 public ImageView ibSetBgColor; // 设置背景色按钮 public ImageButton ibInsertImage; // 插入图片按钮 + public ImageButton ibExtractImage; // 提取图片内容按钮 public TextView tvTitleHint; // 标题提示文字 public EditText etTitle; // 标题输入框 public TextView tvTitleCount; // 字符数提示 @@ -594,6 +595,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); mNoteHeaderHolder.ibInsertImage = (ImageButton) findViewById(R.id.add_img_btn); mNoteHeaderHolder.ibInsertImage.setOnClickListener(this); + mNoteHeaderHolder.ibExtractImage = (ImageButton) findViewById(R.id.extract_img_btn); + mNoteHeaderHolder.ibExtractImage.setOnClickListener(this); mNoteHeaderHolder.tvTitleHint = (TextView) findViewById(R.id.tv_title_hint); mNoteHeaderHolder.etTitle = (EditText) findViewById(R.id.et_title); mNoteHeaderHolder.etTitle.addTextChangedListener(new TextWatcher() { @@ -795,6 +798,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, mFontSizeSelector.setVisibility(View.GONE); } else if (id == R.id.add_img_btn) { insertImage(); + } else if (id == R.id.extract_img_btn) { + extractImageContent(); } else if (id == R.id.btn_bold) { // 处理加粗按钮点击 mNoteEditor.toggleBold(); @@ -1981,4 +1986,143 @@ public class NoteEditActivity extends Activity implements OnClickListener, showToast(R.string.error_out_of_memory); } } + + /** + * 提取图片内容 + */ + private void extractImageContent() { + // 检查当前笔记中是否有图片 + String content = mNoteEditor.getText().toString(); + if (!content.contains("[IMAGE:")) { + showToast(R.string.error_no_image_in_note); + return; + } + + // 提取所有图片路径 + final java.util.List imagePaths = new java.util.ArrayList<>(); + int startIndex = 0; + while (true) { + int imageStart = content.indexOf("[IMAGE:", startIndex); + if (imageStart == -1) { + break; + } + int imageEnd = content.indexOf("]", imageStart); + if (imageEnd == -1) { + break; + } + String imagePath = content.substring(imageStart + 7, imageEnd); + imagePaths.add(imagePath); + startIndex = imageEnd + 1; + } + + if (imagePaths.isEmpty()) { + showToast(R.string.error_no_image_in_note); + return; + } + + // 如果只有一张图片,直接提取 + if (imagePaths.size() == 1) { + extractImageContentFromPath(imagePaths.get(0)); + return; + } + + // 如果有多张图片,让用户选择 + final String[] imageOptions = new String[imagePaths.size()]; + for (int i = 0; i < imagePaths.size(); i++) { + imageOptions[i] = "Image " + (i + 1); + } + + new android.app.AlertDialog.Builder(this) + .setTitle("Select Image") + .setItems(imageOptions, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String selectedImagePath = imagePaths.get(which); + extractImageContentFromPath(selectedImagePath); + } + }) + .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .show(); + } + + /** + * 从指定路径提取图片内容 + */ + private void extractImageContentFromPath(final String imagePath) { + // 显示加载提示 + final android.app.AlertDialog loadingDialog = new android.app.AlertDialog.Builder(this) + .setTitle("Loading") + .setMessage("Extracting image content...") + .setCancelable(false) + .create(); + loadingDialog.show(); + + // 加载图片 + ImageHelper imageHelper = new ImageHelper(this); + final Bitmap bitmap = imageHelper.loadImage(imagePath); + + if (bitmap == null) { + loadingDialog.dismiss(); + showToast(R.string.error_failed_to_load_image); + return; + } + + // 调用AI服务提取图片内容 + net.micode.notes.tool.AIService.extractImageContent(bitmap, new net.micode.notes.tool.AIService.ExtractImageContentCallback() { + @Override + public void onSuccess(final String extractedContent) { + runOnUiThread(new Runnable() { + @Override + public void run() { + loadingDialog.dismiss(); + + // 显示提取结果对话框 + new android.app.AlertDialog.Builder(NoteEditActivity.this) + .setTitle(R.string.dialog_title_extracted_content) + .setMessage(extractedContent) + .setPositiveButton(R.string.dialog_button_insert, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // 将提取的内容插入到笔记中 + int cursorPosition = mNoteEditor.getSelectionStart(); + Editable editable = mNoteEditor.getEditableText(); + editable.insert(cursorPosition, "\n" + extractedContent + "\n"); + mNoteEditor.setSelection(cursorPosition + extractedContent.length() + 2); + + // 更新WorkingNote的内容 + getWorkingText(); + updateWordCount(); + + showToast(R.string.info_content_inserted); + } + }) + .setNegativeButton(R.string.dialog_button_cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .show(); + } + }); + } + + @Override + public void onFailure(final String errorMessage) { + runOnUiThread(new Runnable() { + @Override + public void run() { + loadingDialog.dismiss(); + showToast(R.string.error_extract_image_content_failed); + Log.e(TAG, "Extract image content failed: " + errorMessage); + } + }); + } + }); + } } diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index 64dfc2f..3d3c90f 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -28,6 +28,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; @@ -83,6 +84,8 @@ import net.micode.notes.widget.NoteWidgetProvider_2x; import net.micode.notes.widget.NoteWidgetProvider_4x; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -218,13 +221,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt // 处理从相册选择图片的结果 if (data != null && data.getData() != null) { try { - // 保存图片路径 - mCurrentBackgroundType = BACKGROUND_TYPE_ALBUM; - mCurrentBackgroundPath = data.getData().toString(); - // 更新背景 - updateBackground(); - // 保存设置 - saveBackgroundSetting(); + // 将相册图片复制到应用内部存储 + String internalPath = saveImageToInternalStorage(data.getData()); + if (!TextUtils.isEmpty(internalPath)) { + // 保存内部存储路径 + mCurrentBackgroundType = BACKGROUND_TYPE_ALBUM; + mCurrentBackgroundPath = internalPath; + // 更新背景 + updateBackground(); + // 保存设置 + saveBackgroundSetting(); + } else { + Toast.makeText(this, "图片保存失败", Toast.LENGTH_SHORT).show(); + } } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "图片加载失败", Toast.LENGTH_SHORT).show(); @@ -1640,8 +1649,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } else if (BACKGROUND_TYPE_ALBUM.equals(mCurrentBackgroundType) && !TextUtils.isEmpty(mCurrentBackgroundPath)) { // 使用相册图片作为背景 try { - android.graphics.Bitmap bitmap = android.graphics.BitmapFactory.decodeStream( - getContentResolver().openInputStream(android.net.Uri.parse(mCurrentBackgroundPath))); + android.graphics.Bitmap bitmap = null; + // 检查路径类型 + if (mCurrentBackgroundPath.startsWith("content://")) { + // 如果是URI字符串 + bitmap = android.graphics.BitmapFactory.decodeStream( + getContentResolver().openInputStream(android.net.Uri.parse(mCurrentBackgroundPath))); + } else { + // 如果是内部存储路径 + bitmap = android.graphics.BitmapFactory.decodeFile(mCurrentBackgroundPath); + } + if (bitmap != null) { // 计算屏幕尺寸 android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics(); @@ -1750,4 +1768,42 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }); } + + /** + * 将图片保存到应用内部存储 + * @param uri 图片的URI + * @return 保存后的内部存储路径 + */ + private String saveImageToInternalStorage(Uri uri) { + try { + // 创建内部存储目录 + File directory = new File(getFilesDir(), "backgrounds"); + if (!directory.exists()) { + directory.mkdirs(); + } + + // 创建输出文件 + String fileName = "background_" + System.currentTimeMillis() + ".jpg"; + File outputFile = new File(directory, fileName); + + // 复制图片 + InputStream inputStream = getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(outputFile); + + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + } + + inputStream.close(); + outputStream.close(); + + // 返回文件路径 + return outputFile.getAbsolutePath(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } } diff --git a/src/main/res/layout/note_edit.xml b/src/main/res/layout/note_edit.xml index b529e4d..849137d 100644 --- a/src/main/res/layout/note_edit.xml +++ b/src/main/res/layout/note_edit.xml @@ -94,6 +94,16 @@ android:background="@drawable/bg_btn_insert_image" android:contentDescription="@string/menu_insert_image" android:padding="12dp" /> + Word count large Word count too large + + No image found in the note + Loading + Extracting image content... + Failed to load image + Extracted Content + Insert + Cancel + Content inserted successfully + Failed to extract image content +