You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xiaomi/xiaomi-src/main/java/net/micode/notes/ui/NoteEditActivity.java

1631 lines
70 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.Manifest;//qxq:
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;//qxq
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.speech.tts.TextToSpeech;
import android.support.annotation.RequiresApi;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
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;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.ui.translate_demo.RespondBean;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.FileNotFoundException;//qxq:报错类型
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.micode.notes.ui.translate_demo.MD5Utils;
import net.micode.notes.ui.translate_demo.BaiduTranslateService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
/**
* 顾名思义,本类用于对便签的编辑
* 头部视图的ViewHolder类用于存储头部视图中的UI组件引用。
* 继承的都是系统内部与监听相关的类
*/
public static void setmChanged(Stack mChanged){//设置成员变量
NoteEditActivity.mChanged = mChanged;
}
private class HeadViewHolder {
public TextView tvModified; // 显示修改日期的文本视图
public ImageView ivAlertIcon; // 提示图标图像视图
public TextView tvAlertDate; // 显示提醒日期的文本视图
public ImageView ibSetBgColor; // 设置背景颜色的图像视图
private TextView tvNum;//qxq:在头部视图holder里声明文本视图用于统计字符编辑器edittext直接借用下面的mNoteeditor
}
/**
* 背景选择按钮与其对应选中状态图标的映射。
*/
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {
// 初始化背景选择按钮映射
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
/**
* 背景选择按钮选中状态与其对应图标的映射。
*/
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
// 初始化背景选择按钮选中状态映射
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
/**
* 字号选择按钮与其对应字体大小的映射。
*/
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static {
// 初始化字号选择按钮映射
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
/**
* 字号选择按钮选中状态与其对应图标的映射。
*/
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
// 初始化字号选择按钮选中状态映射
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
}
private static final String TAG = "NoteEditActivity"; // 日志标签
private HeadViewHolder mNoteHeaderHolder; // 头部视图的ViewHolder
private View mHeadViewPanel; // 头部视图面板
private View mNoteBgColorSelector; // 笔记背景颜色选择器
private View mFontSizeSelector; // 字号选择器
private EditText mNoteEditor; // 笔记编辑器
private View mNoteEditorPanel; // 笔记编辑器面板
private WorkingNote mWorkingNote; // qxq:当前正在编辑的笔记,实例化
private SharedPreferences mSharedPrefs; // 共享偏好设置存储keyvalue的xml文件
private int mFontSizeId; // 当前选中的字体大小资源ID
private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; // 字体大小偏好设置键
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; // 快捷图标标题的最大长度
public static final String TAG_CHECKED = String.valueOf('\u221A'); // 标记为已检查的字符串
public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); // 标记为未检查的字符串
private LinearLayout mEditTextList; // 编辑文本列表是线性布局
private String mUserQuery; // 用户查询字符串
private Pattern mPattern; // 正则表达式模式
private final int PHOTO_REQUEST = 1;//qxq:定义请求类型:插入图片
private static Stack mChanged;//hzk:unachieved
private TextToSpeech mTTS;//实现为全局变量,用以之后的朗读
private AlertDialog alertDialog2;//字体功能实现
private CharSequence restore_translate = null;//存储翻译结果的字符序列
private boolean mIsRvoke = false;//是否撤销的标志
//qxq:加入请求存储权限代码
private static final int PERMISSION_REQUEST_CODE = 100;
private void requestStoragePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, PERMISSION_REQUEST_CODE);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予,可以访问图片
Toast.makeText(this, "已获得存储权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "需要存储权限才能访问图片", Toast.LENGTH_SHORT).show();
}
}
}
//qxq:end
@Override
protected void onCreate(Bundle savedInstanceState) {//qxq:初始化活动Bundle变量是Android系统用于存储恢复Activity状态的参数
super.onCreate(savedInstanceState);//super引用父类方法进行初始化
this.setContentView(R.layout.note_edit); // 设置活动的视图布局
// 检查实例状态是否被保存,如果未保存且初始化活动状态失败,则结束该活动
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources(); // 初始化资源
count();
speak();
//qxq:
final ImageButton add_img_btn = (ImageButton) findViewById(R.id.add_img_btn);//qxq:获取按钮实例,并绑定xml中控件和此处图片按钮变量
add_img_btn.setOnClickListener(new View.OnClickListener() {//qxq:这个按钮被触发时,设置了点击监听器函数
@Override
public void onClick(View view) {
Log.d(TAG, "onClick: click add image button");
Intent loadImage = new Intent(Intent.ACTION_GET_CONTENT);//qxq:点击时创建Intent打开系统图片选择器
loadImage.addCategory(Intent.CATEGORY_OPENABLE);//qxq:Used to indicate that an intent only wants URIs that can be opened with ContentResolver.
loadImage.setType("image/*");//qxq:过滤之后类型为image/*
startActivityForResult(loadImage, PHOTO_REQUEST);//qxq:启动图片选择器,等待返回结果
}
});
}
/*
* 两个函数分别实现朗读按钮控件和控件触发函数并在oncreate中执行
*
* */
private void speak()
{
final Button speak =findViewById(R.id.spkBtn);
mTTS = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
mTTS.setLanguage(Locale.US);//US为
mTTS.setPitch(1.5f);// 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
mTTS.setSpeechRate(0.5f);
}
}
});
speak.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String str=mNoteEditor.getText().toString();
str=operateText(str);
str=cutOfimage(str);
mTTS.speak(str,TextToSpeech.QUEUE_FLUSH,null,"1");
}
});
}
private void count()
{
mNoteEditor.addTextChangedListener(new TextWatcher() {
int currentLength = 0;
String str;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mNoteHeaderHolder.tvNum.setText("字符数:" + currentLength);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//currentLength = mNoteEditor.getText().length();
str = mNoteEditor.getText().toString();
str = operateText(str);
str = cutOfimage(str);
currentLength= str.length();
}//在onTextChanged()方法中通过获取编辑框的文本长度并将其赋值给currentLength变量以便在afterTextChanged()方法中使用。
@Override
public void afterTextChanged(Editable s) {//qxq:监听文本变化并显示在tvNum中
mNoteHeaderHolder.tvNum.setText("字符数:" + currentLength);
}
});
}
public String operateText(String str){
String dest = "";
Pattern p = Pattern.compile("\\s*|t|r|n");
Matcher m = p.matcher(str);
dest = m.replaceAll("");
return dest;
}
public String cutOfimage(String str) {
String dest = str;
int index1 = dest.indexOf("[local]");
int index2 = dest.indexOf("[/local]");
while (index1 != -1 && index2 != -1) {
dest = dest.substring(0, index1) + dest.substring(index2 + 8);
index1 = dest.indexOf("[local]");
index2 = dest.indexOf("[/local]");
}
return dest;
}
private void convertToImage() {
requestStoragePermission();//qxq:确保增加权限
NoteEditText noteEditText = (NoteEditText) findViewById(R.id.note_edit_view);//根据id寻找
Editable editable = noteEditText.getText();
String noteText = editable.toString();
int length = editable.length();
for(int i = 0; i < length; i++) {
for(int j = i; j < length; j++) {//qxq:通过穷尽所有可能的字符串,进行查找满足的图片路径
String img_fragment = noteText.substring(i, j+1);
if(img_fragment.length() > 15 && img_fragment.endsWith("[/local]") && img_fragment.startsWith("[local]")){
int limit = 7;
int len = img_fragment.length()-15;
String path = img_fragment.substring(limit,limit+len);
Bitmap bitmap = null;
Log.d(TAG, "图片的路径是:"+path);
File imageFile = new File(path);//qxq
//qxqcontent://media/picker/0/com.android.providers.media.photopicker/media/1000000041
/* ContentResolver resolver = getContentResolver();
String uriString = "content://media/picker/0/com.android.providers.media.photopicker/media/1000000041";
Uri uri = Uri.parse(uriString);
try {
bitmap = BitmapFactory.decodeStream(resolver.openInputStream(uri));//2.解码图片
Log.d(TAG, "onActivityResult: originalUri is: "+uri);
} catch (FileNotFoundException e) {
Log.d(TAG, "onActivityResult: get file_exception");
e.printStackTrace();
}//qxq:end*/
Bitmap bitmap2=null;
try {
bitmap2 = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
}catch(Exception e){
e.printStackTrace();
Log.d(TAG, "图片路径bitmap2不可decode且路径是"+path);
}
if(bitmap2!=null){
Log.d(TAG, "图片bitmap2不为null,且图片路径为:"+imageFile.getAbsolutePath());
ImageSpan imageSpan = new ImageSpan(NoteEditActivity.this, bitmap2);
String ss = "[local]" + path + "[/local]";
SpannableString spannableString = new SpannableString(ss);
spannableString.setSpan(imageSpan, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Log.d(TAG, "Create spannable string success!");
Editable edit_text = noteEditText.getEditableText();
edit_text.delete(i,i+len+15);
edit_text.insert(i, spannableString);
}//qxq:end
try {
bitmap = BitmapFactory.decodeFile(path);
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "图片路径不可decode且路径是"+path);
}
if(bitmap!=null){
Log.d(TAG, "图片不为null,且图片路径为:"+path);
ImageSpan imageSpan = new ImageSpan(NoteEditActivity.this, bitmap);
String ss = "[local]" + path + "[/local]";
SpannableString spannableString = new SpannableString(ss);
spannableString.setSpan(imageSpan, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Log.d(TAG, "Create spannable string success!");
Editable edit_text = noteEditText.getEditableText();
edit_text.delete(i,i+len+15);
edit_text.insert(i, spannableString);
}
}
}
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {//qxq这一块涉及解码intent里的originalUri和上传到数据库服务器
super.onActivityResult(requestCode, resultCode, intent);
ContentResolver resolver = getContentResolver();
switch (requestCode) {
case PHOTO_REQUEST:
Uri originalUri = intent.getData();
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(resolver.openInputStream(originalUri));//2.解码图片
Log.d(TAG, "onActivityResult: originalUri is: "+originalUri);
} catch (FileNotFoundException e) {
Log.d(TAG, "onActivityResult: get file_exception");
e.printStackTrace();
}
if (bitmap != null) {
Log.d(TAG, "onActivityResult: bitmap is not null");
ImageSpan imageSpan = new ImageSpan(NoteEditActivity.this, bitmap);//创建ImageSpan对象
String path = getPath(this, originalUri);//获取图片路径这里将uri转换为了pathstring型
String img_fragment = "[local]" + path + "[/local]";//将图片路径转换为img_fragment
SpannableString spannableString = new SpannableString(img_fragment);//创建SpannableString对象
spannableString.setSpan(imageSpan, 0, img_fragment.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
NoteEditText e = (NoteEditText) findViewById(R.id.note_edit_view);//获取NoteEditText对象
int index = e.getSelectionStart();//获取选中的位置
Log.d(TAG, "Index是: " + index);
Editable edit_text = e.getEditableText();//获取可编辑的文本
edit_text.insert(index, spannableString);//在选中的位置插入img_fragment这里是路径
mWorkingNote.setmContent(e.getText().toString());//设置笔记内容
ContentResolver contentResolver = getContentResolver();//获取ContentResolver对象
ContentValues contentValues = new ContentValues();
final long id = mWorkingNote.getNoteId();
contentValues.put("snippet", mWorkingNote.getContent());//存入数据库
contentResolver.update(Uri.parse("content://micode_notes/note"), contentValues, "_id=?", new String[]{"" + id});
ContentValues contentValues1 = new ContentValues();
contentValues1.put("content", mWorkingNote.getContent());
contentResolver.update(Uri.parse("content://micode_notes/data"), contentValues1, "mime_type=? and note_id=?", new String[]{"vnd.android.cursor.item/text_note", "" + id});
} else {
Toast.makeText(NoteEditActivity.this, "获取图片失败", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;//判断是否是安卓7.0及以上
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {//判断是否是文档uri
if (isMediaDocument(uri)) {//判断是否是媒体文档
final String docId = DocumentsContract.getDocumentId(uri);//获取文档id
final String[] split = docId.split(":");//将文档id分割为数组
final String type = split[0];//获取文档类型
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// Media
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
//qxq:翻译
public void translate() {
final EditText editable = findViewById(R.id.note_edit_view);
final Button get_local = findViewById(R.id.translate);
get_local.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
Button trans1 = new Button(NoteEditActivity.this);
Button trans2 = new Button(NoteEditActivity.this);
Button trans3 = new Button(NoteEditActivity.this);
trans1.setText("中文翻译为英文");
trans2.setText("英文翻译为中文");
trans3.setText("还原");
LinearLayout linear = new LinearLayout(NoteEditActivity.this);
linear.setOrientation(LinearLayout.VERTICAL);
linear.addView(trans1);
linear.addView(trans2);
linear.addView(trans3);
AlertDialog.Builder builder = new AlertDialog.Builder(NoteEditActivity.this);
builder.setView(linear);
builder.setTitle("请选择翻译模式");
AlertDialog choose_trans = builder.create();
choose_trans.show();
trans1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
restore_translate = editable.getText();
translate_z2u();
Toast.makeText(NoteEditActivity.this, "中文翻译为英文", Toast.LENGTH_SHORT).show();
}
});
trans2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
restore_translate = editable.getText();
translate_u2z();
Toast.makeText(NoteEditActivity.this, "英文翻译为中文", Toast.LENGTH_SHORT).show();
}
});
trans3.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (restore_translate == null || restore_translate.toString().equals(editable.getText().toString())) {
Toast.makeText(NoteEditActivity.this, "无可还原内容", Toast.LENGTH_SHORT).show();
} else {
editable.setText(restore_translate);
Toast.makeText(NoteEditActivity.this, "已还原", Toast.LENGTH_SHORT).show();
}
}
});
}
});
}
public void translate_z2u() {
final EditText editable = findViewById(R.id.note_edit_view);
String word = editable.getText().toString();
word = word.replaceAll("\\n", "//");
String from = "auto";
String to = "en";
String appid = "20221021001406789";
String salt = (int) (Math.random() * 100 + 1) + "";
String key = "eWky8SSKgL99Dh4rHMog";
String secretKey = appid + word + salt + key;
String sign = MD5Utils.getMD5Code(secretKey);
Log.d(TAG, "secretKey" + secretKey);
Log.d(TAG, "sign: " + sign);
Retrofit retrofitBaidu = new Retrofit.Builder()
.baseUrl("https://fanyi-api.baidu.com/api/trans/vip/")
.addConverterFactory(GsonConverterFactory.create())
.build();
BaiduTranslateService baiduTranslateService = retrofitBaidu.create(BaiduTranslateService.class);
retrofit2.Call<RespondBean> call = baiduTranslateService.translate(word, from, to, appid, salt, sign);
call.enqueue(new Callback<RespondBean>() {
@Override
public void onResponse(retrofit2.Call<RespondBean> call, Response<RespondBean> response) {
Log.d(TAG, "onResponse: 请求成功");
RespondBean respondBean = response.body();
String result = respondBean.getTrans_result().get(0).getDst();
editable.setText(result);
Log.d(TAG, "中译英结果" + result);
}
@Override
public void onFailure(retrofit2.Call<RespondBean> call, Throwable t) {
Log.d(TAG, "onResponse: 请求失败 " + t);
}
});
}
public void translate_u2z() {
final EditText editable = findViewById(R.id.note_edit_view);
String word = editable.getText().toString();
word = word.replaceAll("\\n", "//");
Log.d(TAG, word);
String from = "auto";
String to = "zh";
String appid = "20221021001406789";
String salt = (int) (Math.random() * 100 + 1) + "";
String key = "eWky8SSKgL99Dh4rHMog";
String secretKey = appid + word + salt + key;
String sign = MD5Utils.getMD5Code(secretKey);
Log.d(TAG, "secretKey" + secretKey);
Log.d(TAG, "sign: " + sign);
Retrofit retrofitBaidu = new Retrofit.Builder()
.baseUrl("https://fanyi-api.baidu.com/api/trans/vip/")
.addConverterFactory(GsonConverterFactory.create())
.build();
BaiduTranslateService baiduTranslateService = retrofitBaidu.create(BaiduTranslateService.class);
retrofit2.Call<RespondBean> call = baiduTranslateService.translate(word, from, to, appid, salt, sign);
call.enqueue(new Callback<RespondBean>() {
@Override
public void onResponse(retrofit2.Call<RespondBean> call, Response<RespondBean> response) {
Log.d(TAG, "onResponse: 请求成功");
RespondBean respondBean = response.body();
String result = respondBean.getTrans_result().get(0).getDst();
editable.setText(result);
Log.d(TAG, "中译英结果" + result);
}
@Override
public void onFailure(retrofit2.Call<RespondBean> call, Throwable t) {
Log.d(TAG, "onResponse: 请求失败 " + t);
}
});
}
/**
* 当活动被系统销毁后,为了恢复之前的状态,此方法会被调用。
* 主要用于处理活动重新创建时的数据恢复。
*
* @param savedInstanceState 包含之前保存状态的Bundle如果活动之前保存了状态这里会传入非空的Bundle。
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 检查是否有保存的状态并且包含必要的UID信息用于恢复活动状态
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
// 使用intent尝试恢复活动状态如果失败则结束该活动
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity"); // 日志记录,表示活动状态正在从被杀死的状态恢复
}
}
/**
* 初始化活动状态根据传入的Intent确定是查看笔记、新建笔记还是编辑笔记。
*
* @param intent 传入的Intent包含了动作类型和相关数据。
* @return boolean 返回true表示成功初始化活动状态false表示初始化失败。
*/
private boolean initActivityState(Intent intent) {
// 如果用户指定的是查看笔记的动作但未提供笔记ID则跳转到笔记列表活动
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
// 从搜索结果开始
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
// 检查指定的笔记在数据库中是否可见
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
// 加载指定ID的笔记
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
// 隐藏软键盘
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())) {
// 处理新建或编辑笔记的情况
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// 解析通话记录笔记
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
// 根据电话号码和通话日期尝试获取已有笔记ID
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
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;
}
} else {
// 创建新的通话记录笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
// 创建普通空笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
// 显示软键盘
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
// 不支持的Intent动作
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
// 设置笔记状态改变的监听器
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
/**
* 当Activity恢复到前台时调用。
* 主要负责初始化笔记界面。
*/
@Override
protected void onResume() {//当Activity恢复到前台时调用。主要负责初始化笔记界面。
super.onResume();
initNoteScreen();
}
/**
* 初始化笔记界面的函数。
* 该函数设置笔记编辑器的外观,根据笔记类型切换到相应的模式,设置背景和头部信息,
* 以及处理提醒头部的显示。
*/
private void initNoteScreen() {//初始化笔记界面的函数。该函数设置笔记编辑器的外观,根据笔记类型切换到相应的模式,设置背景和头部信息,以及处理提醒头部的显示
// 设置编辑器的文本外观
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
// 根据当前笔记的类型,切换到列表模式或高亮查询结果模式
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
// 隐藏所有背景选择器
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
// 设置标题和编辑区域的背景
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// 设置修改时间
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR));
// 显示提醒头部信息当前禁用因DateTimePicker未准备好
showAlertHeader();
//qxq:增加转换功能
convertToImage();
}
/**
* 显示提醒头部信息的方法。
* 如果当前笔记设置了提醒,该方法将根据提醒时间显示相对的时间或者过期信息。
*/
private void showAlertHeader() {
// 处理提醒显示
if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis();
// 如果提醒时间已过,显示提醒已过期的信息
if (time > mWorkingNote.getAlertDate()) {
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else {
// 否则,显示相对时间
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
}
// 设置提醒视图可见
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else {
// 如果没有设置提醒,隐藏提醒视图
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
}
}
/**
* 当Activity接收到新的Intent时调用。
* 用于根据新的Intent重新初始化Activity状态。
*
* @param intent 新的Intent
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
/**
* 保存Activity状态时调用。
* 用于保存当前编辑的笔记,如果该笔记还未保存到数据库,则首先保存它。
* 并且保存当前笔记的ID到Bundle中。
*
* @param outState 用于保存Activity状态的Bundle
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 如果当前编辑的笔记不存在于数据库中,即还未保存,先保存它
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 保存笔记ID
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
/**
* 分发触摸事件。如果触摸事件不在指定视图范围内,则隐藏该视图。
*
* @param ev 触摸事件
* @return 如果事件被消费则返回true否则返回false
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果背景颜色选择器可见且不在触摸范围内,则隐藏它并消费事件
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
// 如果字体大小选择器可见且不在触摸范围内,则隐藏它并消费事件
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
// 如果上述条件都不满足,则将事件传递给父类处理
return super.dispatchTouchEvent(ev);
}
/**
* 判断触摸点是否在指定视图范围内。
*
* @param view 视图
* @param ev 触摸事件
* @return 如果触摸点在视图范围内则返回true否则返回false
*/
private boolean inRangeOfView(View view, MotionEvent ev) {
int[] location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {
return false;// 判断触摸点是否在视图外
}
return true;
}
/**
* 初始化资源,包括视图和偏好设置。
*/
private void initResources() {// 初始化头部视图和相关组件
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
mNoteHeaderHolder.tvNum = (TextView) findViewById(R.id.note_num);//qxq:初始化绑定视图变量
// 初始化编辑器和相关组件
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
//下面这段代码的作用是为sBgSelectorBtnsMap中的所有ImageView对象设置点击事件监听器。
// 当某个ImageView被点击时会触发与当前类关联的onClick()方法。
//sBgSelectorBtnsMap是一个Map对象它存储了一些键值对key对应id每个值则未在这段代码中给出。
// 这种数据结构通常用于在Android应用中存储和操作UI元素例如这里的ImageView。
// 通过键即ImageView的id可以快速找到并操作对应的UI元素。
// 具体来说这段代码遍历了sBgSelectorBtnsMap中的所有键并为每个键对应的ImageView设置了一个点击事件监听器。
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}// 设置背景选择器按钮点击监听器
mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
}// 设置字体大小选择器按钮点击监听器
;
// 从偏好设置中读取字体大小
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
// 修复存储在偏好设置中的字体大小资源ID的错误
if (mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
// 初始化编辑列表
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
translate();
}
/**
* 在Activity即将失去焦点并进入后台时的回调方法用于执行一些与保存数据、清除状态等相关的操作。
*/
@Override
protected void onPause() {//暂停时保存笔记数据并清除设置状态。
super.onPause();
// 保存笔记数据
if (saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();//调用clearSettingState()方法来清除一些设置状态。具体的逻辑需要根据实际需求来实现,可能包括清除临时数据、重置一些标志位等。
}
/**
* 更新小部件显示,和桌面小工具的同步
*/
private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 根据小部件类型设置对应的类
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
// 设置小部件ID
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]{
mWorkingNote.getWidgetId()
});
// 发送广播更新小部件
sendBroadcast(intent);
// 设置结果为OK
setResult(RESULT_OK, intent);
}
/**
* 点击事件的处理函数。
*
* @param v 被点击的视图对象。
*/
public void onClick(View v) {
int id = v.getId();
// 如果点击的是设置背景颜色的按钮
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
// 根据当前笔记的背景颜色,设置对应的色板按钮可见
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {
// 如果点击的是背景色板上的颜色
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE); // 隐藏当前选择的背景颜色
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); // 更新笔记的背景颜色
mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏背景颜色选择器
} else if (sFontSizeBtnsMap.containsKey(id)) {
// 如果点击的是字体大小按钮
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); // 隐藏当前选择的字体大小
mFontSizeId = sFontSizeBtnsMap.get(id); // 更新选择的字体大小
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); // 保存选择的字体大小到SharedPreferences
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); // 设置新选择的字体大小按钮为可见
// 根据当前笔记是否为清单模式,进行相应的文本更新
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
getWorkingText();
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
}
mFontSizeSelector.setVisibility(View.GONE); // 隐藏字体大小选择器
}
}
/**
* 按下返回键时的处理函数。
*/
@Override
public void onBackPressed() {
// 尝试清除设置状态,如果成功,则不执行保存笔记操作
if (clearSettingState()) {
return;
}
// 保存笔记并执行父类的onBackPressed()方法结束当前Activity
saveNote();
super.onBackPressed();
}
/**
* 尝试清除设置状态(背景颜色选择或字体大小选择)。
*
* @return 如果成功清除设置状态返回true否则返回false。
*/
private boolean clearSettingState() {
// 如果背景颜色选择器可见则隐藏它并返回true
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { // 如果字体大小选择器可见则隐藏它并返回true
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return false; // 没有可见的设置状态需要清除返回false
}
/**
* 当背景颜色发生变化时的处理函数。
*/
public void onBackgroundColorChanged() {
// 根据当前笔记的背景颜色,设置对应的色板按钮可见,并更新编辑器及标题栏的背景颜色
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
}
/**
* 准备选项菜单的函数。
*
* @param menu 选项菜单对象。
* @return 总是返回true。
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 如果Activity正在结束则不进行任何操作
if (isFinishing()) {
return true;
}
// 尝试清除设置状态
clearSettingState();
menu.clear(); // 清除菜单项
// 根据笔记所属的文件夹,加载不同的菜单资源
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
// 根据当前笔记的清单模式,更新“清单模式”菜单项的标题
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
// 根据笔记是否有提醒,更新“删除提醒”菜单项的可见性
if (mWorkingNote.hasClockAlert()) {
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
return true;
}
/**
* 处理选项菜单项的选择事件。
*
* @param item 选中的菜单项
* @return 总是返回true表示事件已处理。
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_new_note:
// 创建新笔记
createNewNote();
break;
case R.id.menu_delete:
// 显示删除笔记的确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_note));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 确认删除当前笔记并结束当前活动
deleteCurrentNote();
finish();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.menu_font_size:
// 显示字体大小选择器
mFontSizeSelector.setVisibility(View.VISIBLE);
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
break;
case R.id.menu_list_mode:
// 切换笔记的列表模式
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
break;
case R.id.menu_share:
// 获取当前编辑的笔记内容并分享
getWorkingText();
sendTo(this, mWorkingNote.getContent());
break;
case R.id.menu_send_to_desktop:
// 将笔记发送到桌面
sendToDesktop();
break;
case R.id.menu_alert:
// 设置提醒
setReminder();
break;
case R.id.menu_delete_remind:
// 删除提醒设置
mWorkingNote.setAlertDate(0, false);
break;
default:
break;
}
return true;
}
/**
* 弹出日期时间选择器,用于设置提醒时间。
*/
private void setReminder() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
// 用户设定时间后,设置提醒
mWorkingNote.setAlertDate(date, true);
}
});
d.show();
}
/**
* 分享笔记到支持 {@link Intent#ACTION_SEND} 操作和 {@text/plain} 类型的应用。
*
* @param context 上下文
* @param info 要分享的信息
*/
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info);
intent.setType("text/plain");
context.startActivity(intent);
}
/**
* 首先保存当前正在编辑的笔记然后启动一个新的NoteEditActivity用于创建新笔记。
*/
private void createNewNote() {
// 首先保存当前笔记
saveNote();
// 安全地开始一个新的NoteEditActivity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
startActivity(intent);
}
/**
* 删除当前正在编辑的笔记。
* 如果笔记存在于数据库中,并且当前不是同步模式,将直接删除该笔记;
* 如果处于同步模式,则将笔记移动到回收站文件夹。
*/
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) {
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id);
} else {
Log.d(TAG, "Wrong note id, should not happen");
}
if (!isSyncMode()) {
// 非同步模式下直接删除笔记
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "Delete Note error");
}
} else {
// 同步模式下将笔记移动到回收站
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
}
mWorkingNote.markDeleted(true);
}
/**
* 判断当前是否为同步模式。
* 同步模式是指在设置中配置了同步账户名。
*
* @return 如果配置了同步账户名返回true否则返回false。
*/
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
/**
* 处理时钟提醒变更事件。
* 首先检查当前笔记是否已保存,未保存则先保存。
* 如果笔记存在根据set参数设置或取消提醒。
* 如果笔记不存在即无有效ID记录错误并提示用户输入内容。
*
* @param date 提醒的日期时间戳
* @param set 是否设置提醒
*/
public void onClockAlertChanged(long date, boolean set) {
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
showAlertHeader();
if (!set) {
alarmManager.cancel(pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
}
} else {
Log.e(TAG, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock);
}
}
/**
* 更新小部件显示。
*/
public void onWidgetChanged() {
updateWidget();
}
/**
* 当删除某个编辑框中的文本时的处理逻辑。
* 重新设置后续编辑框的索引,并将删除的文本添加到前一个或当前编辑框中。
*
* @param index 被删除文本的编辑框索引
* @param text 被删除的文本内容
*/
public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount();
if (childCount == 1) {
return;
}
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
if (index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
}
int length = edit.length();
edit.append(text);
edit.requestFocus();
edit.setSelection(length);
}
/**
* 当在编辑框中按下“Enter”键时的处理逻辑。
* 在列表中添加一个新的编辑框,并重新设置后续编辑框的索引。
*
* @param index 当前编辑框的索引
* @param text 当前编辑框中的文本内容
*/
public void onEditTextEnter(int index, String text) {
if (index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
}
View view = getListItem(text, index);
mEditTextList.addView(view, index);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.requestFocus();
edit.setSelection(0);
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);
}
}
/**
* 切换到列表模式。
* 将文本分割成多行,并为每行创建一个列表项,展示在编辑文本列表中。
*
* @param text 要转换成列表模式的文本,每行代表一个列表项。
*/
private void switchToListMode(String text) {
// 清空当前的视图
mEditTextList.removeAllViews();
// 使用换行符分割文本,创建列表项
String[] items = text.split("\n");
int index = 0;
for (String item : items) {
// 忽略空行
if (!TextUtils.isEmpty(item)) {
mEditTextList.addView(getListItem(item, index));
index++;
}
}
// 添加一个空的列表项作为占位符
mEditTextList.addView(getListItem("", index));
// 请求焦点以便于编辑
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
// 隐藏编辑器,显示列表
mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);
}
/**
* 高亮显示查询结果。
* 在给定的文本中,根据用户查询字符串高亮显示匹配的部分。
*
* @param fullText 完整的文本。
* @param userQuery 用户的查询字符串。
* @return 包含高亮显示的文本的Spannable对象。
*/
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
// 如果有查询字符串,则进行高亮处理
if (!TextUtils.isEmpty(userQuery)) {
mPattern = Pattern.compile(userQuery);
Matcher m = mPattern.matcher(fullText);
int start = 0;
while (m.find(start)) {
// 设置高亮背景
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
start = m.end();
}
}
return spannable;
}
/**
* 创建列表项视图。
* 为列表模式创建并配置一个包含文本编辑框和复选框的视图。
*
* @param item 列表项的文本内容。
* @param index 列表项的索引。
* @return 配置好的列表项视图。
*/
private View getListItem(String item, int index) {
// 加载列表项布局
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 设置文本样式
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
// 复选框的监听器,用于切换文本的划线状态
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
});
// 根据文本前缀设置复选框状态和文本内容
if (item.startsWith(TAG_CHECKED)) {
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
// 设置文本变化监听和索引
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
// 设置带有查询结果高亮的文本
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
/**
* 根据文本内容是否为空,切换复选框的可见性。
*
* @param index 列表项索引。
* @param hasText 列表项是否包含文本。
*/
public void onTextChange(int index, boolean hasText) {
if (index >= mEditTextList.getChildCount()) {
Log.e(TAG, "Wrong index, should not happen");
return;
}
// 根据文本内容决定复选框的可见性
if (hasText) {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
} else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
}
}
/**
* 在切换编辑模式和列表模式时更新UI。
*
* @param oldMode 旧的编辑模式。
* @param newMode 新的编辑模式。
*/
public void onCheckListModeChanged(int oldMode, int newMode) {
// 切换到列表模式
if (newMode == TextNote.MODE_CHECK_LIST) {
switchToListMode(mNoteEditor.getText().toString());
} else {
// 切换回编辑模式
if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
""));
}
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE);
mNoteEditor.setVisibility(View.VISIBLE);
}
convertToImage();//qxq在切换模式时也调用
}
/**
* 根据当前列表项的选中状态,构建并返回工作文本。
*
* @return 是否有已选中的列表项。
*/
private boolean getWorkingText() {//qxq:得到编辑的文本框内容
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 构建带有选中状态前缀的文本
if (!TextUtils.isEmpty(edit.getText())) {
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
mWorkingNote.setWorkingText(sb.toString());
} else {
mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
}
return hasChecked;
}
/**
* 保存笔记。
* 更新笔记内容并保存。
*
* @return 是否成功保存笔记。
*/
private boolean saveNote() {
getWorkingText();
boolean saved = mWorkingNote.saveNote();
if (saved) {
// 设置结果为成功,以便外部调用者知道保存操作的状态
setResult(RESULT_OK);
}
return saved;
}
/**
* 将当前编辑的笔记发送到桌面。首先会检查当前编辑的笔记是否已存在于数据库中,
* 如果不存在,则先保存。如果存在,会创建一个快捷方式放在桌面。
*/
private void sendToDesktop() {
// 检查当前编辑的笔记是否存在于数据库,若不存在则先保存
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 如果笔记存在于数据库有noteId则创建快捷方式
if (mWorkingNote.getNoteId() > 0) {
Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,
makeShortcutIconTitle(mWorkingNote.getContent()));
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
sender.putExtra("duplicate", true);
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
showToast(R.string.info_note_enter_desktop);
sendBroadcast(sender);
} else {
// 如果用户未输入任何内容,无法创建快捷方式,提醒用户输入内容
Log.e(TAG, "Send to desktop error");
showToast(R.string.error_note_empty_for_send_to_desktop);
}
}
/**
* 根据笔记内容生成快捷方式的标题。移除内容中的已选和未选标签,并确保标题长度不超过上限。
*
* @param content 符合快捷方式图标标题要求的笔记内容
* @return 标题字符串
*/
private String makeShortcutIconTitle(String content) {
content = content.replace(TAG_CHECKED, "");
content = content.replace(TAG_UNCHECKED, "");
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
}
/**
* 显示一个Toast消息。
*
* @param resId 资源ID指向要显示的字符串
*/
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
/**
* 显示一个指定时长的Toast消息。
*
* @param resId 资源ID指向要显示的字符串
* @param duration 显示时长可以是Toast.LENGTH_SHORT或Toast.LENGTH_LONG
*/
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
}
}
//qxq: