@ -19,22 +19,38 @@ package net.micode.notes.ui;
import android.app.Activity ;
import android.app.AlarmManager ;
import android.app.AlertDialog ;
import android.app.Dialog ;
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.database.Cursor ;
import android.graphics.Bitmap ;
import android.graphics.BitmapFactory ;
import android.graphics.Paint ;
import android.graphics.Typeface ;
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 ;
@ -43,10 +59,12 @@ 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 ;
@ -65,15 +83,32 @@ import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x ;
import net.micode.notes.widget.NoteWidgetProvider_4x ;
import java.io.FileNotFoundException ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Locale ;
import java.util.Map ;
import java.util.Stack ;
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
import net.micode.notes.translate.BaiduTranslateService ;
import net.micode.notes.translate.RespondBean ;
import net.micode.notes.translate.MD5Utils ;
import retrofit2.Call ;
import retrofit2.Callback ;
import retrofit2.Response ;
import retrofit2.Retrofit ;
import retrofit2.converter.gson.GsonConverterFactory ;
import android.speech.tts.TextToSpeech.OnInitListener ;
public class NoteEditActivity extends Activity implements OnClickListener ,
NoteSettingChangedListener , OnTextViewChangeListener {
public static void setmChanged ( Stack mChanged ) {
NoteEditActivity . mChanged = mChanged ;
}
private class HeadViewHolder {
public TextView tvModified ;
@ -149,22 +184,338 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private String mUserQuery ;
private Pattern mPattern ;
private CharSequence restore_translate = null ;
private boolean mIsRvoke = false ;
private TextToSpeech mTTS ;
private static Stack mChanged ;
private final int PHOTO_REQUEST = 1 ;
private Dialog alertDialog2 ;
Context context ;
private static final int MAX_TIME_OF_RVOKE_TIME = 100 ;
private final int MAX_OF_RVOKE_TIME = 100 ;
public void translateChtoEn ( ) {
//准备请求百度翻译接口需要的参数
final EditText editable = findViewById ( R . id . note_edit_view ) ;
String word = editable . getText ( ) . toString ( ) ;
word = word . replaceAll ( "\\n" , "//" ) ;
String from = "auto" ; //源语种 en 英语 zh 中文
//String中英文占用一个字节, 中文占用两个字节,
//利用String的这个存储特性可以用来判断String中有没有中文。
// to = "zh"; //没有汉字 英译中
String to = "en" ; //含有汉字 中译英
String appid = "20240103001929054" ; //appid 管理控制台有
String salt = ( int ) ( Math . random ( ) * 100 + 1 ) + "" ; //随机数这里范围是[0,100]整数无强制要求
String key = "Xa_yB5ihrTIaG8Nlv4S6" ; //密钥 管理控制台有
String secretKey = appid + word + salt + key ; // secretKey = appid+q+salt+密钥
String sign = MD5Utils . getMD5Code ( secretKey ) ; // 签名 = secretKey 的MD5加密32位字母小写
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 ( Call < RespondBean > call , Response < RespondBean > response ) {
//请求成功
Log . d ( TAG , "onResponse: 请求成功" ) ;
RespondBean respondBean = response . body ( ) ; //返回的JSON字符串对应的对象
String result = respondBean . getTrans_result ( ) . get ( 0 ) . getDst ( ) ; //获取翻译的字符串String
editable . setText ( result ) ;
Log . d ( TAG , "中译英结果" + result ) ;
}
@Override
public void onFailure ( Call < RespondBean > call , Throwable t ) {
//请求失败 打印异常
Log . d ( TAG , "onResponse: 请求失败 " + t ) ;
}
} ) ;
}
public void translateEntoCh ( ) {
//准备请求百度翻译接口需要的参数
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" ; //源语种 en 英语 zh 中文
//String中英文占用一个字节, 中文占用两个字节,
//利用String的这个存储特性可以用来判断String中有没有中文。
// to = "zh"; //没有汉字 英译中
String to = "zh" ; //含有汉字 英译中
String appid = "20240103001929054" ; //appid 管理控制台有
String salt = ( int ) ( Math . random ( ) * 100 + 1 ) + "" ; //随机数这里范围是[0,100]整数无强制要求
String key = "Xa_yB5ihrTIaG8Nlv4S6" ; //密钥 管理控制台有
String secretKey = appid + word + salt + key ; // secretKey = appid+q+salt+密钥
String sign = MD5Utils . getMD5Code ( secretKey ) ; // 签名 = secretKey 的MD5加密32位字母小写
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 ( Call < RespondBean > call , Response < RespondBean > response ) {
//请求成功
Log . d ( TAG , "onResponse: 请求成功" ) ;
RespondBean respondBean = response . body ( ) ; //返回的JSON字符串对应的对象
String result = respondBean . getTrans_result ( ) . get ( 0 ) . getDst ( ) ; //获取翻译的字符串String
editable . setText ( result ) ;
Log . d ( TAG , "英译中结果" + result ) ;
}
@Override
public void onFailure ( Call < RespondBean > call , Throwable t ) {
//请求失败 打印异常
Log . d ( TAG , "onResponse: 请求失败 " + t ) ;
}
} ) ;
}
public void doTranslate ( ) {
final EditText editable = findViewById ( R . id . note_edit_view ) ;
final Button get_local = findViewById ( R . id . translate ) ; //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 ) ; //定义一个AlertDialog生成器
builder . setView ( linear ) ; //附上线性表结构
builder . setTitle ( "请选择翻译模式" ) ; //提示语句
AlertDialog choose_trans = builder . create ( ) ; //生成一个AlertDialog的对话框
choose_trans . show ( ) ; //展示
trans1 . setOnClickListener ( new OnClickListener ( ) { //如果点击第一个按钮,则触发
@Override
public void onClick ( View v ) {
restore_translate = editable . getText ( ) ;
Log . d ( TAG , "666" + restore_translate ) ;
choose_trans . dismiss ( ) ;
if ( restore_translate = = null ) {
Toast . makeText ( NoteEditActivity . this , "当前无可翻译内容" , Toast . LENGTH_SHORT ) . show ( ) ;
}
else {
translateChtoEn ( ) ;
Toast . makeText ( NoteEditActivity . this , "中文翻译为英文" , Toast . LENGTH_SHORT ) . show ( ) ;
}
}
} ) ;
trans2 . setOnClickListener ( new OnClickListener ( ) { //如果点击第二个按钮,则触发
@Override
public void onClick ( View v ) {
restore_translate = editable . getText ( ) ;
Log . d ( TAG , "666" + restore_translate ) ;
choose_trans . dismiss ( ) ;
if ( restore_translate = = null ) {
Toast . makeText ( NoteEditActivity . this , "当前无可翻译内容" , Toast . LENGTH_SHORT ) . show ( ) ;
}
else {
translateEntoCh ( ) ;
Toast . makeText ( NoteEditActivity . this , "英文翻译为中文" , Toast . LENGTH_SHORT ) . show ( ) ;
}
}
} ) ;
trans3 . setOnClickListener ( new OnClickListener ( ) { //如果点击第三个按钮,则触发
@Override
public void onClick ( View v ) {
choose_trans . dismiss ( ) ;
Log . d ( TAG , "666" + restore_translate ) ;
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 ( ) ; //功能语句
}
}
} ) ;
}
} ) ;
}
//图片地址转换为图片
private void convertToImage ( ) {
NoteEditText noteEditText = ( NoteEditText ) findViewById ( R . id . note_edit_view ) ;
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 + + ) {
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 ) ;
try {
bitmap = BitmapFactory . decodeFile ( path ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
if ( bitmap ! = null ) {
Log . d ( TAG , "图片不为null" ) ;
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 ) {
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.解码图片
} 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 ) ;
String path = getPath ( this , originalUri ) ;
String img_fragment = "[local]" + path + "[/local]" ;
SpannableString spannableString = new SpannableString ( img_fragment ) ;
spannableString . setSpan ( imageSpan , 0 , img_fragment . length ( ) , Spannable . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
NoteEditText e = ( NoteEditText ) findViewById ( R . id . note_edit_view ) ;
int index = e . getSelectionStart ( ) ;
Log . d ( TAG , "Index是: " + index ) ;
Editable edit_text = e . getEditableText ( ) ;
edit_text . insert ( index , spannableString ) ;
mWorkingNote . setmContent ( e . getText ( ) . toString ( ) ) ;
ContentResolver contentResolver = getContentResolver ( ) ;
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 ;
if ( isKitKat & & DocumentsContract . isDocumentUri ( context , uri ) ) {
if ( isMediaDocument ( uri ) ) {
final String docId = DocumentsContract . getDocumentId ( uri ) ;
final String [ ] split = docId . split ( ":" ) ;
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 ( ) ) ;
}
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
this . setContentView ( R . layout . note_edit ) ;
setmChanged ( new Stack < > ( ) ) ;
if ( savedInstanceState = = null & & ! initActivityState ( getIntent ( ) ) ) {
finish ( ) ;
return ;
}
initResources ( ) ;
final ImageButton add_img_btn = ( ImageButton ) findViewById ( R . id . add_img_btn ) ;
add_img_btn . setOnClickListener ( new View . OnClickListener ( ) {
@Override
public void onClick ( View view ) {
Log . d ( TAG , "onClick: click add image button" ) ;
Intent loadImage = new Intent ( Intent . ACTION_GET_CONTENT ) ;
loadImage . addCategory ( Intent . CATEGORY_OPENABLE ) ;
loadImage . setType ( "image/*" ) ;
startActivityForResult ( loadImage , PHOTO_REQUEST ) ;
}
} ) ;
}
/ * *
* Current activity may be killed when the memory is low . Once it is killed , for another time
* user load this activity , we should restore the former state
* /
//用于在Activity被系统销毁后恢复其状态
@Override
protected void onRestoreInstanceState ( Bundle savedInstanceState ) {
super . onRestoreInstanceState ( savedInstanceState ) ;
@ -179,12 +530,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
}
//该方法主要用于初始化Activity的状态, 根据传入的Intent的不同动作( action) 执行不同的操作。
private boolean initActivityState ( Intent intent ) {
/ * *
* If the user specified the { @link Intent # ACTION_VIEW } but not provided with id ,
* then jump to the NotesListActivity
* /
mWorkingNote = null ;
//打开某个笔记的操作
if ( TextUtils . equals ( Intent . ACTION_VIEW , intent . getAction ( ) ) ) {
long noteId = intent . getLongExtra ( Intent . EXTRA_UID , 0 ) ;
mUserQuery = "" ;
@ -196,7 +549,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
noteId = Long . parseLong ( intent . getStringExtra ( SearchManager . EXTRA_DATA_KEY ) ) ;
mUserQuery = intent . getStringExtra ( SearchManager . USER_QUERY ) ;
}
//DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE) 是一个方法调用
// 用于检查给定的 noteId 是否存在于笔记数据库中
if ( ! DataUtils . visibleInNoteDatabase ( getContentResolver ( ) , noteId , Notes . TYPE_NOTE ) ) {
Intent jump = new Intent ( this , NotesListActivity . class ) ;
startActivity ( jump ) ;
@ -204,6 +558,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
finish ( ) ;
return false ;
} else {
//如果指定的笔记 ID 存在于笔记数据库中,则调用 WorkingNote.load() 方法,根据指定的笔记 ID 加载笔记对象,并将结果赋值给 mWorkingNote
mWorkingNote = WorkingNote . load ( this , noteId ) ;
if ( mWorkingNote = = null ) {
Log . e ( TAG , "load note failed with note id" + noteId ) ;
@ -215,7 +570,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
WindowManager . LayoutParams . SOFT_INPUT_STATE_HIDDEN
| WindowManager . LayoutParams . SOFT_INPUT_ADJUST_RESIZE ) ;
} else if ( TextUtils . equals ( Intent . ACTION_INSERT_OR_EDIT , intent . getAction ( ) ) ) {
//需要创建或编辑一个笔记
// New note
//从 Intent 中获取一些额外的信息,如文件夹 ID、小部件 ID、小部件类型和背景资源 ID。这些信息用于确定新笔记的位置和外观。
long folderId = intent . getLongExtra ( Notes . INTENT_EXTRA_FOLDER_ID , 0 ) ;
int widgetId = intent . getIntExtra ( Notes . INTENT_EXTRA_WIDGET_ID ,
AppWidgetManager . INVALID_APPWIDGET_ID ) ;
@ -268,6 +625,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
initNoteScreen ( ) ;
}
//用于初始化笔记屏幕
private void initNoteScreen ( ) {
mNoteEditor . setTextAppearance ( this , TextAppearanceResources
. getTexAppearanceResource ( mFontSizeId ) ) ;
@ -293,6 +651,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* is not ready
* /
showAlertHeader ( ) ;
convertToImage ( ) ;
}
private void showAlertHeader ( ) {
@ -362,7 +721,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
return true ;
}
//初始化一些资源
private void initResources ( ) {
mHeadViewPanel = findViewById ( R . id . note_title ) ;
mNoteHeaderHolder = new HeadViewHolder ( ) ;
@ -374,11 +733,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
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对象, 它存储了一些键值对, 其中每个sBgSelectorBtnsMap是一个Map对象, 它存储了一些键值对, 其中每个键对应一个ImageView对象的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 ) ;
@ -391,10 +756,150 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* The id may larger than the length of resources , in this case ,
* return the { @link ResourceParser # BG_DEFAULT_FONT_SIZE }
* /
//这段代码是用于获取应用程序的字体大小设置
if ( mFontSizeId > = TextAppearanceResources . getResourcesSize ( ) ) {
mFontSizeId = ResourceParser . BG_DEFAULT_FONT_SIZE ;
}
mEditTextList = ( LinearLayout ) findViewById ( R . id . note_edit_list ) ;
/ *
撤 回 部 分
* /
mNoteEditor . addTextChangedListener ( new TextWatcher ( ) {
@Override
public void beforeTextChanged ( CharSequence s , int start , int count , int after )
{
}
@Override
public void onTextChanged ( CharSequence s , int start , int before , int count ) {
}
@Override
public void afterTextChanged ( Editable s ) { //文本更改后
if ( ! mIsRvoke ) {
saveMyChanged ( ) ;
} else {
mIsRvoke = false ;
}
}
} ) ;
//翻译
doTranslate ( ) ;
//朗读
mTTS = new TextToSpeech ( this , new OnInitListener ( ) {
@Override
public void onInit ( int status ) {
if ( status = = TextToSpeech . SUCCESS ) {
mTTS . setLanguage ( Locale . CHINESE ) ;
mTTS . setPitch ( 1.5f ) ; // 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
mTTS . setSpeechRate ( 0.5f ) ;
}
}
} ) ;
mHeadViewPanel = findViewById ( R . id . note_title ) ;
}
private void texttoSpeech ( ) {
mTTS . speak ( mNoteEditor . getText ( ) . toString ( ) , TextToSpeech . QUEUE_FLUSH , null , null ) ;
}
private void doRevoke ( ) {
int size = mChanged . size ( ) ; //获取当前栈大小
AlertDialog . Builder dialog = new AlertDialog . Builder ( this ) ; //创建一个alertdialog 窗口
dialog . setTitle ( R . string . tips_of_revoke ) ; //设置 title 信息
dialog . setCancelable ( true ) ; //设置为可取消
dialog . setPositiveButton ( "确定" , new DialogInterface . OnClickListener ( ) { //只需要设置一个 OK 键即可
@Override
public void onClick ( DialogInterface dialog , int which ) {
}
} ) ;
mIsRvoke = true ;
if ( size < = 1 ) { //如果栈中元素过少,打印提示信息
dialog . setMessage ( R . string . have_not_input_anything ) ; //提示用户您还没有输入任何信息
dialog . show ( ) ; //显示当前 alertdialog
return ;
}
else {
mNoteEditor . setText ( ( CharSequence ) mChanged . elementAt ( size - 2 ) ) ; //在textview 中设置撤销的内容
mNoteEditor . setSelection ( mNoteEditor . length ( ) ) ;
mChanged . removeElementAt ( size - 1 ) ; //删除元素
if ( size = = 2 ) {
dialog . setMessage ( R . string . can_not_revoke ) ; //如果只有一次操作,那么提示用户不能再撤销了
dialog . show ( ) ; //显示当前 alertdialog
}
}
}
private void saveMyChanged ( ) {
SpannableString text = new SpannableString ( mNoteEditor . getText ( ) ) ; //用 getText方法获取每次编辑的内容
if ( mChanged . size ( ) > = MAX_TIME_OF_RVOKE_TIME ) { //如果栈中的数据大于最大撤销次数,就把第一次修改的内容删除
mChanged . removeElementAt ( 0 ) ;
}
mChanged . add ( text ) ; //然后把本次修改的内容加入栈中
}
public void showSingleAlertDialog ( ) {
final String [ ] items = { "默认-普通" , "默认-非衬线" , "默认-衬线" , "默认-等宽" , "仿宋" , "黑体" , "楷体" , "隶书" , "微软雅黑" } ;
AlertDialog . Builder alertBuilder = new AlertDialog . Builder ( this ) ;
alertBuilder . setTitle ( "请选择字体" ) ;
alertBuilder . setSingleChoiceItems ( items , 0 , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialogInterface , int i ) {
switch ( i ) {
case 0 :
mNoteEditor . setTypeface ( Typeface . DEFAULT ) ;
break ;
case 1 :
mNoteEditor . setTypeface ( Typeface . SANS_SERIF ) ;
break ;
case 2 :
mNoteEditor . setTypeface ( Typeface . SERIF ) ;
break ;
case 3 :
mNoteEditor . setTypeface ( Typeface . MONOSPACE ) ;
break ;
case 4 :
Typeface typeface0 = Typeface . createFromAsset ( getAssets ( ) , "front/simfang.ttf" ) ;
mNoteEditor . setTypeface ( typeface0 ) ;
break ;
case 5 :
Typeface typeface1 = Typeface . createFromAsset ( getAssets ( ) , "front/simhei.ttf" ) ;
mNoteEditor . setTypeface ( typeface1 ) ;
break ;
case 6 :
Typeface typeface2 = Typeface . createFromAsset ( getAssets ( ) , "front/simkai.ttf" ) ;
mNoteEditor . setTypeface ( typeface2 ) ;
break ;
case 7 :
Typeface typeface3 = Typeface . createFromAsset ( getAssets ( ) , "front/SIMLI.TTF" ) ;
mNoteEditor . setTypeface ( typeface3 ) ;
break ;
case 8 :
Typeface typeface4 = Typeface . createFromAsset ( getAssets ( ) , "front/msyh.ttc" ) ;
mNoteEditor . setTypeface ( typeface4 ) ;
break ;
}
Toast . makeText ( NoteEditActivity . this , items [ i ] , Toast . LENGTH_SHORT ) . show ( ) ;
}
} ) ;
alertBuilder . setPositiveButton ( "确定" , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialogInterface , int i ) {
alertDialog2 . dismiss ( ) ;
}
} ) ;
alertBuilder . setNegativeButton ( "取消" , new DialogInterface . OnClickListener ( )
{
@Override
public void onClick ( DialogInterface dialogInterface , int i ) {
alertDialog2 . dismiss ( ) ;
}
} ) ;
alertDialog2 = alertBuilder . create ( ) ;
alertDialog2 . show ( ) ;
}
@Override
@ -424,13 +929,19 @@ public class NoteEditActivity extends Activity implements OnClickListener,
sendBroadcast ( intent ) ;
setResult ( RESULT_OK , intent ) ;
}
/ *
这 段 代 码 是 一 个 事 件 处 理 函 数 , 用 于 处 理 按 钮 点 击 事 件 。 根 据 不 同 的 按 钮 ID 执 行 不 同 的 操 作 。
* /
public void onClick ( View v ) {
int id = v . getId ( ) ;
//如果按钮ID等于R.id.btn_set_bg_color, 则表示用户点击了设置背景颜色的按钮。
// 此时,将背景颜色选择器的可见性设置为可见,并显示当前选中的背景颜色对应的控件
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 ) ) {
//如果按钮ID在sBgSelectorBtnsMap中存在, 表示用户点击了某个背景颜色按钮。
// 此时, 隐藏当前选中的背景颜色对应的控件, 将工作笔记的背景颜色设置为该按钮对应的ID, 并将背景颜色选择器的可见性设置为不可见。
findViewById ( sBgSelectorSelectionMap . get ( mWorkingNote . getBgColorId ( ) ) ) . setVisibility (
View . GONE ) ;
mWorkingNote . setBgColorId ( sBgSelectorBtnsMap . get ( id ) ) ;
@ -444,8 +955,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
getWorkingText ( ) ;
switchToListMode ( mWorkingNote . getContent ( ) ) ;
} else {
mNoteEditor . setTextAppearance ( this ,
TextAppearanceResources . getTexAppearanceResource ( mFontSizeId ) ) ;
mNoteEditor . setTextAppearance ( this , TextAppearanceResources . getTexAppearanceResource ( mFontSizeId ) ) ;
}
mFontSizeSelector . setVisibility ( View . GONE ) ;
}
@ -479,6 +989,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mHeadViewPanel . setBackgroundResource ( mWorkingNote . getTitleBgResId ( ) ) ;
}
//该方法的主要作用是根据当前的工作笔记( mWorkingNote) 的属性来准备和更新菜单项。
@Override
public boolean onPrepareOptionsMenu ( Menu menu ) {
if ( isFinishing ( ) ) {
@ -486,6 +997,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
clearSettingState ( ) ;
menu . clear ( ) ;
//工作笔记的文件夹ID
if ( mWorkingNote . getFolderId ( ) = = Notes . ID_CALL_RECORD_FOLDER ) {
getMenuInflater ( ) . inflate ( R . menu . call_note_edit , menu ) ;
} else {
@ -505,7 +1017,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
@Override
//这段代码是一个用于处理菜单项点击事件的方法onOptionsItemSelected(MenuItem item), 当用户点击菜单项时, 会根据菜单项的ID执行相应的操作。以下是对代码的解释:
public boolean onOptionsItemSelected ( MenuItem item ) {
int itemId = item . getItemId ( ) ;
if ( itemId = = R . id . menu_new_note ) {
@ -524,7 +1035,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} ) ;
builder . setNegativeButton ( android . R . string . cancel , null ) ;
builder . show ( ) ;
} else if ( itemId = = R . id . menu_font_size ) { //修改字体的大小
} else if ( itemId = = R . id . menu_font_size ) {
mFontSizeSelector . setVisibility ( View . VISIBLE ) ;
findViewById ( sFontSelectorSelectionMap . get ( mFontSizeId ) ) . setVisibility ( View . VISIBLE ) ;
} else if ( itemId = = R . id . menu_list_mode ) {
@ -539,24 +1050,20 @@ public class NoteEditActivity extends Activity implements OnClickListener,
setReminder ( ) ;
} else if ( itemId = = R . id . menu_delete_remind ) {
mWorkingNote . setAlertDate ( 0 , false ) ;
} else if ( itemId = = R . id . menu_revoke ) {
doRevoke ( ) ;
} else if ( itemId = = R . id . menu_voice_speech ) {
Log . d ( TAG , "in" ) ;
texttoSpeech ( ) ;
} else if ( itemId = = R . id . menu_font_setting ) {
showSingleAlertDialog ( ) ;
}
return true ;
}
//onOptionsItemSelected方法是一个覆盖方法, 用于在用户选择某个选项菜单项时被调用。
//
//在这段代码中, 首先获取选项菜单项的ID, 即itemId = item.getItemId()。
//
//然后使用if-else语句对不同的菜单项进行处理。
//
//若itemId等于R.id.menu_new_note, 则调用createNewNote()方法。
//若itemId等于R.id.menu_delete, 则创建一个AlertDialog对话框, 设置标题、图标和消息, 并添加确定按钮和取消按钮的点击事件监听器, 点击确定按钮时调用deleteCurrentNote()方法并结束当前活动。
//若itemId等于R.id.menu_font_size, 则设置字体大小选择器显示, 并根据当前选中的字体大小ID找到对应的视图并设置其可见性为可见。
//若itemId等于R.id.menu_list_mode, 则切换工作笔记的清单模式( 若当前为0, 则切换为检查清单模式, 否则切换为普通文本模式) 。
//若itemId等于R.id.menu_share, 则获取当前工作笔记的文本内容, 并将其发送到指定目标。
//若itemId等于R.id.menu_send_to_desktop, 则发送笔记到桌面。
//若itemId等于R.id.menu_alert, 则设置提醒事项。
//若itemId等于R.id.menu_delete_remind, 则将工作笔记的提醒日期设置为0, 表示删除提醒。
//最后, 返回值为true, 表示已经处理了选择的菜单项。
private void setReminder ( ) {
DateTimePickerDialog d = new DateTimePickerDialog ( this , System . currentTimeMillis ( ) ) ;
d . setOnDateTimeSetListener ( new OnDateTimeSetListener ( ) {
@ -566,15 +1073,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} ) ;
d . show ( ) ;
}
//这段代码定义了一个名为setReminder的私有方法, 用于设置笔记的提醒时间。
//
//在方法内部, 首先创建一个DateTimePickerDialog对象d, 并将当前时间作为默认时间传递给该对象。
//
//接着, 为d设置一个OnDateTimeSetListener监听器, 当用户设置时间时, 会调用该监听器的OnDateTimeSet方法。在该方法中, 调用正在编辑的笔记对象mWorkingNote的setAlertDate方法, 将用户设置的时间转换成毫秒值, 并将其设置为笔记的提醒时间。
//
//最后, 显示d对话框, 让用户选择提醒时间。
//
//因此,这段代码用于弹出一个日期时间选择对话框,让用户设置笔记的提醒时间,并将设置的时间保存到正在编辑的笔记对象中。
/ * *
* Share note to apps that support { @link Intent # ACTION_SEND } action
* and { @text / plain } type
@ -585,49 +1084,50 @@ public class NoteEditActivity extends Activity implements OnClickListener,
intent . setType ( "text/plain" ) ;
context . startActivity ( intent ) ;
}
//这段代码是一个私有方法sendTo, 用于使用系统默认的分享功能将指定的文本信息发送给其他应用程序。
//
//在方法内部, 首先创建一个新的Intent对象, 并将其动作设置为Intent.ACTION_SEND, 表示发送内容。
//
//接着, 通过putExtra方法将要分享的文本信息info放入Intent中, 使用Intent.EXTRA_TEXT作为键。
//
//然后, 使用setType方法将要分享的内容的MIME类型设置为"text/plain",表示纯文本类型。
//
//最后, 通过context.startActivity(intent)启动该Intent, 将文本信息发送给其他应用程序进行处理。
//
//因此, 这段代码用于在给定的Context上下文环境中, 利用系统默认的分享功能将特定的文本信息发送给其他应用程序。
private void createNewNote ( ) { //新建便签
private void createNewNote ( ) {
// Firstly, save current editing notes
//保存当前便签
saveNote ( ) ;
// For safety, start a new NoteEditActivity
finish ( ) ;
//设置链接器
Intent intent = new Intent ( this , NoteEditActivity . class ) ;
//将该活动定义为创建或编辑
intent . setAction ( Intent . ACTION_INSERT_OR_EDIT ) ;
//将运行便签的id添加到INTENT_EXTRA_FOLDER_ID标记中
intent . putExtra ( Notes . INTENT_EXTRA_FOLDER_ID , mWorkingNote . getFolderId ( ) ) ;
//开始活动并链接
startActivity ( intent ) ;
}
private void deleteCurrentNote ( ) { //删除便签
private void deleteCurrentNote ( ) {
//假如当前运行的便签内存有数据
if ( mWorkingNote . existInDatabase ( ) ) {
HashSet < Long > ids = new HashSet < Long > ( ) ;
long id = mWorkingNote . getNoteId ( ) ;
//如果不是头文件夹建立一个hash表把便签id存起来
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" ) ;
}
}
}
//将这些标签的删除标记置为true
mWorkingNote . markDeleted ( true ) ;
}
@ -827,15 +1327,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
return hasChecked ;
}
//段代码是一个私有方法getWorkingText, 用于获取工作笔记的文本内容, 并根据是否为待办事项模式进行相应处理。同时, 该方法返回一个布尔值, 表示在待办事项模式下是否有选中的项目。
//
//在方法内部, 首先检查当前工作笔记mWorkingNote是否为待办事项模式。如果是, 就遍历所有子视图view, 其中包含每个待办事项的复选框和文本编辑框。对于每个非空的文本编辑框, 如果其相应的复选框被选中, 则将文本内容作为已选中的待办事项添加到字符串缓冲区sb中, 并将标记hasChecked设置为true; 否则, 将文本内容作为未选中的待办事项添加到字符串缓冲区sb中。
//
//最后, 将字符串缓冲区sb中的内容作为工作笔记的文本内容设置到mWorkingNote对象中。如果不是待办事项模式, 则将文本编辑器mNoteEditor中的文本内容设置为工作笔记的文本内容。
//
//最终, 该方法返回一个布尔值hasChecked, 表示在待办事项模式下是否有选中的项目。
//
//因此,这段代码用于获取工作笔记的文本内容,并根据是否为待办事项模式进行相应处理并返回结果。
private boolean saveNote ( ) {
getWorkingText ( ) ;
boolean saved = mWorkingNote . saveNote ( ) ;
@ -851,17 +1343,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
return saved ;
}
//这段代码是一个私有方法saveNote, 用于保存当前正在编辑的笔记。
//
//在方法内部, 首先调用getWorkingText方法获取当前正在编辑的笔记的文本内容, 并将其保存到mWorkingNote对象中。
//
//接着, 调用mWorkingNote.saveNote()方法将笔记保存到数据库中, 并将保存结果保存到saved变量中。
//
//如果保存成功, 则调用setResult(RESULT_OK)方法, 设置当前Activity的返回状态为RESULT_OK。这个状态用于标识从编辑状态返回到列表视图时, 是创建新笔记还是编辑已有笔记。
//
//最后,返回保存是否成功的布尔值。
//
//因此,这段代码用于将当前正在编辑的笔记保存到数据库中,并设置返回状态。
private void sendToDesktop ( ) {
/ * *
* Before send message to home , we should make sure that current
@ -896,33 +1378,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
showToast ( R . string . error_note_empty_for_send_to_desktop ) ;
}
}
//这段代码定义了一个名为sendToDesktop的私有方法, 用于将笔记发送到桌面。
//
//在方法内部, 首先检查当前正在编辑的笔记是否存在于数据库中。如果笔记不存在于数据库中, 则调用saveNote()方法保存笔记。
//
//接着, 如果当前正在编辑的笔记具有有效的笔记ID( 即大于0) , 则执行以下操作:
//
//创建一个Intent对象sender用于发送广播。
//
//创建一个Intent对象shortcutIntent, 指定其目标为NoteEditActivity类, 并设置动作为Intent.ACTION_VIEW。
//
//将正在编辑的笔记ID作为附加数据放入shortcutIntent中。
//
//将笔记内容生成适合作为快捷方式图标标题的字符串, 并放入sender中作为附加数据。
//
//将应用程序的图标资源作为快捷方式图标放入sender中。
//
//设置sender的动作为com.android.launcher.action.INSTALL_SHORTCUT, 表示要安装快捷方式。
//
//弹出一个简短的提示消息,提示用户笔记已经进入桌面。
//
//发送广播,安装快捷方式。
//
//如果当前正在编辑的笔记没有有效的笔记ID, 则执行以下操作:
//
//输出一个错误日志,表示发送到桌面出错。
//
//弹出一个提示消息,提醒用户必须输入一些内容才能发送到桌面。
private String makeShortcutIconTitle ( String content ) {
content = content . replace ( TAG_CHECKED , "" ) ;
content = content . replace ( TAG_UNCHECKED , "" ) ;