|
|
|
|
@ -28,7 +28,10 @@ import android.content.DialogInterface;
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.SharedPreferences;
|
|
|
|
|
import android.graphics.Paint;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.AsyncTask;
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.os.Environment;
|
|
|
|
|
import android.preference.PreferenceManager;
|
|
|
|
|
import android.text.Spannable;
|
|
|
|
|
import android.text.SpannableString;
|
|
|
|
|
@ -47,6 +50,7 @@ import android.widget.CheckBox;
|
|
|
|
|
import android.widget.CompoundButton;
|
|
|
|
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
|
|
|
|
import android.widget.EditText;
|
|
|
|
|
import android.widget.ImageButton;
|
|
|
|
|
import android.widget.ImageView;
|
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
@ -65,13 +69,29 @@ import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
|
|
|
|
|
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
|
|
|
|
|
import net.micode.notes.widget.NoteWidgetProvider_2x;
|
|
|
|
|
import net.micode.notes.widget.NoteWidgetProvider_4x;
|
|
|
|
|
|
|
|
|
|
import net.micode.notes.data.AttachmentManager;
|
|
|
|
|
import net.micode.notes.tool.PermissionHelper;
|
|
|
|
|
import com.bumptech.glide.Glide;
|
|
|
|
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Locale;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
import androidx.core.content.FileProvider;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 笔记编辑Activity - 小米便签的核心编辑界面
|
|
|
|
|
* 实现OnClickListener, NoteSettingChangedListener, OnTextViewChangeListener接口
|
|
|
|
|
@ -151,17 +171,27 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
private String mUserQuery; // 用户搜索查询(用于高亮显示)
|
|
|
|
|
private Pattern mPattern; // 用于高亮搜索结果的模式
|
|
|
|
|
|
|
|
|
|
// 附件相关
|
|
|
|
|
private LinearLayout mAttachmentContainer; // 附件容器
|
|
|
|
|
private static final int REQUEST_GALLERY = 1001;
|
|
|
|
|
private static final int REQUEST_CAMERA = 1002;
|
|
|
|
|
private static final int REQUEST_PERMISSION_CAMERA = 1003;
|
|
|
|
|
private static final int REQUEST_PERMISSION_STORAGE = 1004;
|
|
|
|
|
private static final int REQUEST_CAMERA_PREVIEW = 1005;
|
|
|
|
|
private String mCurrentPhotoPath; // 相机拍照临时文件路径
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
this.setContentView(R.layout.note_edit); // 设置布局文件
|
|
|
|
|
|
|
|
|
|
initResources(); // 初始化资源
|
|
|
|
|
|
|
|
|
|
// 初始化Activity状态,如果初始化失败则结束Activity
|
|
|
|
|
if (savedInstanceState == null && !initActivityState(getIntent())) {
|
|
|
|
|
finish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
initResources(); // 初始化资源
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -210,18 +240,32 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
finish();
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
// 加载笔记
|
|
|
|
|
mWorkingNote = WorkingNote.load(this, noteId);
|
|
|
|
|
if (mWorkingNote == null) {
|
|
|
|
|
Log.e(TAG, "load note failed with note id" + noteId);
|
|
|
|
|
finish();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 在后台线程加载笔记,避免阻塞UI线程
|
|
|
|
|
new AsyncTask<Long, Void, WorkingNote>() {
|
|
|
|
|
@Override
|
|
|
|
|
protected WorkingNote doInBackground(Long... params) {
|
|
|
|
|
long id = params[0];
|
|
|
|
|
try {
|
|
|
|
|
return WorkingNote.load(NoteEditActivity.this, id);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "load note failed with note id" + id, e);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(WorkingNote result) {
|
|
|
|
|
if (result != null) {
|
|
|
|
|
mWorkingNote = result;
|
|
|
|
|
setupWorkingNoteAndInitializeUI(Intent.ACTION_VIEW);
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "load note failed");
|
|
|
|
|
finish();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.execute(noteId);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// 隐藏软键盘
|
|
|
|
|
getWindow().setSoftInputMode(
|
|
|
|
|
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
|
|
|
|
|
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
|
|
|
|
}
|
|
|
|
|
// 处理新建或编辑笔记的请求
|
|
|
|
|
else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
|
|
|
|
|
@ -245,45 +289,88 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
// 检查是否已存在相同的通话记录笔记
|
|
|
|
|
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
|
|
|
|
|
phoneNumber, callDate)) > 0) {
|
|
|
|
|
mWorkingNote = WorkingNote.load(this, noteId);
|
|
|
|
|
if (mWorkingNote == null) {
|
|
|
|
|
Log.e(TAG, "load call note failed with note id" + noteId);
|
|
|
|
|
finish();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 在后台线程加载通话记录笔记
|
|
|
|
|
new AsyncTask<Long, Void, WorkingNote>() {
|
|
|
|
|
@Override
|
|
|
|
|
protected WorkingNote doInBackground(Long... params) {
|
|
|
|
|
long id = params[0];
|
|
|
|
|
try {
|
|
|
|
|
return WorkingNote.load(NoteEditActivity.this, id);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "load call note failed with note id" + id, e);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(WorkingNote result) {
|
|
|
|
|
if (result != null) {
|
|
|
|
|
mWorkingNote = result;
|
|
|
|
|
setupWorkingNoteAndInitializeUI(Intent.ACTION_INSERT_OR_EDIT);
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "load call note failed");
|
|
|
|
|
finish();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.execute(noteId);
|
|
|
|
|
} else {
|
|
|
|
|
// 创建新的通话记录笔记
|
|
|
|
|
// 创建新的通话记录笔记(无需数据库操作,直接在UI线程处理)
|
|
|
|
|
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
|
|
|
|
|
widgetType, bgResId);
|
|
|
|
|
mWorkingNote.convertToCallNote(phoneNumber, callDate);
|
|
|
|
|
setupWorkingNoteAndInitializeUI(Intent.ACTION_INSERT_OR_EDIT);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 创建普通笔记
|
|
|
|
|
// 创建普通笔记(无需数据库操作,直接在UI线程处理)
|
|
|
|
|
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
|
|
|
|
|
bgResId);
|
|
|
|
|
setupWorkingNoteAndInitializeUI(Intent.ACTION_INSERT_OR_EDIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示软键盘
|
|
|
|
|
getWindow().setSoftInputMode(
|
|
|
|
|
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
|
|
|
|
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "Intent not specified action, should not support");
|
|
|
|
|
finish();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置笔记设置变化监听器
|
|
|
|
|
mWorkingNote.setOnSettingStatusChangedListener(this);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置WorkingNote并初始化UI
|
|
|
|
|
*/
|
|
|
|
|
private void setupWorkingNoteAndInitializeUI(String action) {
|
|
|
|
|
if (mWorkingNote != null) {
|
|
|
|
|
// 设置笔记设置变化监听器
|
|
|
|
|
mWorkingNote.setOnSettingStatusChangedListener(this);
|
|
|
|
|
|
|
|
|
|
// 根据不同的操作类型设置软键盘模式
|
|
|
|
|
if (TextUtils.equals(Intent.ACTION_VIEW, action)) {
|
|
|
|
|
// 隐藏软键盘
|
|
|
|
|
getWindow().setSoftInputMode(
|
|
|
|
|
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
|
|
|
|
|
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
|
|
|
|
} else if (TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, action)) {
|
|
|
|
|
// 显示软键盘
|
|
|
|
|
getWindow().setSoftInputMode(
|
|
|
|
|
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
|
|
|
|
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化UI界面
|
|
|
|
|
initNoteScreen();
|
|
|
|
|
|
|
|
|
|
// 加载附件
|
|
|
|
|
loadAttachments();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onResume() {
|
|
|
|
|
super.onResume();
|
|
|
|
|
initNoteScreen(); // 初始化笔记界面
|
|
|
|
|
// 每次恢复时应用老年人模式
|
|
|
|
|
ElderModeUtils.applyElderMode(this, findViewById(android.R.id.content));
|
|
|
|
|
// 只有当mWorkingNote已经初始化完成时才初始化界面
|
|
|
|
|
if (mWorkingNote != null) {
|
|
|
|
|
initNoteScreen(); // 初始化笔记界面(已包含老年人模式应用)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -319,6 +406,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
|
|
|
|
|
// 显示闹钟提醒头部
|
|
|
|
|
showAlertHeader();
|
|
|
|
|
|
|
|
|
|
// 在设置字体外观后应用老年人模式,确保老年人模式的字体设置不被覆盖
|
|
|
|
|
ElderModeUtils.applyElderMode(this, mNoteEditor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -442,6 +532,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
|
|
|
|
|
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
|
|
|
|
|
|
|
|
|
|
// 初始化附件容器
|
|
|
|
|
mAttachmentContainer = (LinearLayout) findViewById(R.id.note_attachment_container);
|
|
|
|
|
|
|
|
|
|
// 应用老年人模式
|
|
|
|
|
ElderModeUtils.applyElderMode(this, findViewById(android.R.id.content));
|
|
|
|
|
}
|
|
|
|
|
@ -630,6 +723,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
|
|
|
|
|
TextNote.MODE_CHECK_LIST : 0);
|
|
|
|
|
break;
|
|
|
|
|
case R.id.menu_insert_image:
|
|
|
|
|
handleInsertImage(); // 插入图片
|
|
|
|
|
break;
|
|
|
|
|
case R.id.menu_share:
|
|
|
|
|
getWorkingText(); // 获取工作文本
|
|
|
|
|
sendTo(this, mWorkingNote.getContent()); // 分享笔记
|
|
|
|
|
@ -1049,4 +1145,293 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
private void showToast(int resId, int duration) {
|
|
|
|
|
Toast.makeText(this, resId, duration).show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==================== 附件相关方法 ====================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加载附件列表并显示
|
|
|
|
|
*/
|
|
|
|
|
private void loadAttachments() {
|
|
|
|
|
if (mWorkingNote == null || mWorkingNote.getNoteId() <= 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<AttachmentManager.Attachment> attachments = mWorkingNote.getAttachments();
|
|
|
|
|
if (attachments.isEmpty()) {
|
|
|
|
|
mAttachmentContainer.setVisibility(View.GONE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mAttachmentContainer.setVisibility(View.VISIBLE);
|
|
|
|
|
mAttachmentContainer.removeAllViews();
|
|
|
|
|
|
|
|
|
|
for (AttachmentManager.Attachment attachment : attachments) {
|
|
|
|
|
addAttachmentView(attachment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加附件视图
|
|
|
|
|
* @param attachment 附件数据
|
|
|
|
|
*/
|
|
|
|
|
private void addAttachmentView(AttachmentManager.Attachment attachment) {
|
|
|
|
|
View view = LayoutInflater.from(this).inflate(R.layout.image_item, mAttachmentContainer, false);
|
|
|
|
|
ImageView imageView = view.findViewById(R.id.image_view);
|
|
|
|
|
ImageButton deleteButton = view.findViewById(R.id.btn_delete);
|
|
|
|
|
|
|
|
|
|
// 使用Glide加载图片
|
|
|
|
|
Glide.with(this)
|
|
|
|
|
.load(new File(attachment.filePath))
|
|
|
|
|
.override(800, 800)
|
|
|
|
|
.centerCrop()
|
|
|
|
|
.transition(DrawableTransitionOptions.withCrossFade())
|
|
|
|
|
.into(imageView);
|
|
|
|
|
|
|
|
|
|
// 长按删除
|
|
|
|
|
view.setOnLongClickListener(v -> {
|
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
|
.setTitle(R.string.delete_image)
|
|
|
|
|
.setMessage(R.string.confirm_delete_image)
|
|
|
|
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
|
|
|
|
if (mWorkingNote.deleteAttachment(attachment.id)) {
|
|
|
|
|
mAttachmentContainer.removeView(view);
|
|
|
|
|
if (mAttachmentContainer.getChildCount() == 0) {
|
|
|
|
|
mAttachmentContainer.setVisibility(View.GONE);
|
|
|
|
|
}
|
|
|
|
|
showToast(R.string.image_deleted);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
|
.show();
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 删除按钮点击
|
|
|
|
|
deleteButton.setOnClickListener(v -> view.performLongClick());
|
|
|
|
|
|
|
|
|
|
mAttachmentContainer.addView(view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理菜单项点击:插入图片
|
|
|
|
|
*/
|
|
|
|
|
private void handleInsertImage() {
|
|
|
|
|
ImageInsertDialogFragment dialog = new ImageInsertDialogFragment();
|
|
|
|
|
dialog.setOnImageInsertListener(new ImageInsertDialogFragment.OnImageInsertListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onGallerySelected() {
|
|
|
|
|
requestGalleryPermission();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onCameraSelected() {
|
|
|
|
|
requestCameraPermission();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
dialog.show(getFragmentManager(), ImageInsertDialogFragment.TAG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 请求相册权限
|
|
|
|
|
*/
|
|
|
|
|
private void requestGalleryPermission() {
|
|
|
|
|
String[] permissions;
|
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
|
|
|
|
|
permissions = new String[]{PermissionHelper.Permissions.READ_MEDIA_IMAGES};
|
|
|
|
|
} else {
|
|
|
|
|
permissions = new String[]{PermissionHelper.Permissions.READ_EXTERNAL_STORAGE};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PermissionHelper.arePermissionsGranted(this, permissions)) {
|
|
|
|
|
openGallery();
|
|
|
|
|
} else {
|
|
|
|
|
PermissionHelper.requestPermissions(this, permissions, REQUEST_PERMISSION_STORAGE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 请求相机权限
|
|
|
|
|
*/
|
|
|
|
|
private void requestCameraPermission() {
|
|
|
|
|
String[] permissions = {PermissionHelper.Permissions.CAMERA};
|
|
|
|
|
if (PermissionHelper.arePermissionsGranted(this, permissions)) {
|
|
|
|
|
openCamera();
|
|
|
|
|
} else {
|
|
|
|
|
PermissionHelper.requestPermissions(this, permissions, REQUEST_PERMISSION_CAMERA);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 打开相册选择图片
|
|
|
|
|
*/
|
|
|
|
|
private void openGallery() {
|
|
|
|
|
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
|
|
|
|
intent.setType("image/*");
|
|
|
|
|
startActivityForResult(intent, REQUEST_GALLERY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 打开相机拍照
|
|
|
|
|
*/
|
|
|
|
|
private void openCamera() {
|
|
|
|
|
Intent takePictureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
|
|
|
|
File photoFile = createImageFile();
|
|
|
|
|
if (photoFile != null) {
|
|
|
|
|
mCurrentPhotoPath = photoFile.getAbsolutePath();
|
|
|
|
|
try {
|
|
|
|
|
Uri photoURI = FileProvider.getUriForFile(this,
|
|
|
|
|
getApplicationContext().getPackageName() + ".fileprovider",
|
|
|
|
|
photoFile);
|
|
|
|
|
takePictureIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoURI);
|
|
|
|
|
startActivityForResult(takePictureIntent, REQUEST_CAMERA);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Failed to get URI from FileProvider", e);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "Failed to create image file");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建临时图片文件
|
|
|
|
|
* @return 文件对象,失败返回null
|
|
|
|
|
*/
|
|
|
|
|
private File createImageFile() {
|
|
|
|
|
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
|
|
|
|
String imageFileName = "JPEG_" + timeStamp + "_";
|
|
|
|
|
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
|
|
|
|
try {
|
|
|
|
|
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
|
|
|
|
|
return image;
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
Log.e(TAG, "Failed to create image file", e);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理权限请求结果
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
|
|
|
PermissionHelper.handlePermissionResult(requestCode, permissions, grantResults,
|
|
|
|
|
new PermissionHelper.OnPermissionResultListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onAllGranted(int requestCode, List<String> grantedPermissions) {
|
|
|
|
|
if (requestCode == REQUEST_PERMISSION_STORAGE) {
|
|
|
|
|
openGallery();
|
|
|
|
|
} else if (requestCode == REQUEST_PERMISSION_CAMERA) {
|
|
|
|
|
openCamera();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDenied(int requestCode, List<String> grantedPermissions, List<String> deniedPermissions) {
|
|
|
|
|
showToast(R.string.permission_denied);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理Activity结果(图片选择/拍照)
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
|
|
|
if (resultCode != RESULT_OK) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (requestCode == REQUEST_GALLERY && data != null) {
|
|
|
|
|
Uri selectedImage = data.getData();
|
|
|
|
|
if (selectedImage != null) {
|
|
|
|
|
processSelectedImage(selectedImage, Notes.ATTACHMENT_TYPE_GALLERY);
|
|
|
|
|
}
|
|
|
|
|
} else if (requestCode == REQUEST_CAMERA) {
|
|
|
|
|
if (mCurrentPhotoPath != null) {
|
|
|
|
|
File photoFile = new File(mCurrentPhotoPath);
|
|
|
|
|
if (photoFile.exists()) {
|
|
|
|
|
showCameraPreviewDialog(photoFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理选择的图片
|
|
|
|
|
* @param imageUri 图片URI
|
|
|
|
|
* @param type 附件类型
|
|
|
|
|
*/
|
|
|
|
|
private void processSelectedImage(Uri imageUri, int type) {
|
|
|
|
|
try {
|
|
|
|
|
File sourceFile = new File(imageUri.getPath());
|
|
|
|
|
if (!sourceFile.exists()) {
|
|
|
|
|
// 如果直接路径不存在,尝试通过ContentResolver获取
|
|
|
|
|
sourceFile = getFileFromUri(imageUri);
|
|
|
|
|
}
|
|
|
|
|
if (sourceFile != null && sourceFile.exists()) {
|
|
|
|
|
long attachmentId = mWorkingNote.addAttachment(type, sourceFile);
|
|
|
|
|
if (attachmentId > 0) {
|
|
|
|
|
loadAttachments();
|
|
|
|
|
showToast(R.string.image_added);
|
|
|
|
|
} else {
|
|
|
|
|
showToast(R.string.failed_to_add_image);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Failed to process image", e);
|
|
|
|
|
showToast(R.string.failed_to_add_image);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从URI获取文件
|
|
|
|
|
* @param uri 图片URI
|
|
|
|
|
* @return 文件对象,失败返回null
|
|
|
|
|
*/
|
|
|
|
|
private File getFileFromUri(Uri uri) {
|
|
|
|
|
// 简化实现:对于content:// URI,直接复制到临时文件
|
|
|
|
|
try {
|
|
|
|
|
InputStream inputStream = getContentResolver().openInputStream(uri);
|
|
|
|
|
if (inputStream == null) return null;
|
|
|
|
|
|
|
|
|
|
File tempFile = File.createTempFile("temp_image", ".jpg", getCacheDir());
|
|
|
|
|
FileOutputStream outputStream = new FileOutputStream(tempFile);
|
|
|
|
|
byte[] buffer = new byte[4096];
|
|
|
|
|
int bytesRead;
|
|
|
|
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
|
|
|
outputStream.write(buffer, 0, bytesRead);
|
|
|
|
|
}
|
|
|
|
|
outputStream.close();
|
|
|
|
|
inputStream.close();
|
|
|
|
|
return tempFile;
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
Log.e(TAG, "Failed to get file from URI", e);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示相机预览对话框
|
|
|
|
|
* @param photoFile 拍照生成的图片文件
|
|
|
|
|
*/
|
|
|
|
|
private void showCameraPreviewDialog(File photoFile) {
|
|
|
|
|
CameraPreviewDialogFragment dialog = new CameraPreviewDialogFragment();
|
|
|
|
|
dialog.setPhotoFile(photoFile);
|
|
|
|
|
dialog.setOnCameraPreviewListener(new CameraPreviewDialogFragment.OnCameraPreviewListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onConfirm(File photoFile) {
|
|
|
|
|
// 确认使用照片,添加到附件
|
|
|
|
|
processSelectedImage(Uri.fromFile(photoFile), Notes.ATTACHMENT_TYPE_CAMERA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onRetake() {
|
|
|
|
|
// 重拍,重新打开相机
|
|
|
|
|
openCamera();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
dialog.show(getFragmentManager(), CameraPreviewDialogFragment.TAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|