@ -16,25 +16,32 @@
package net.micode.notes.ui ;
import android.Manifest ;
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. ContentUris ;
import android.content. Context ;
import android. content.DialogInterface ;
import android. content.Intent ;
import android. content.SharedPreferences ;
import android.content. * ;
import android.content. pm.PackageManager ;
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.text.Editable ;
import android.text.Spannable ;
import android.text.SpannableString ;
import android.text.TextUtils ;
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,15 +50,10 @@ import android.view.MotionEvent;
import android.view.View ;
import android.view.View.OnClickListener ;
import android.view.WindowManager ;
import android.widget.CheckBox ;
import android.widget.CompoundButton ;
import android.widget.* ;
import android.widget.CompoundButton.OnCheckedChangeListener ;
import android.widget.EditText ;
import android.widget.ImageView ;
import android.widget.LinearLayout ;
import android.widget.TextView ;
import android.widget.Toast ;
import androidx.core.content.ContextCompat ;
import net.micode.notes.R ;
import net.micode.notes.data.Notes ;
import net.micode.notes.data.Notes.TextNote ;
@ -64,7 +66,9 @@ 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 org.apache.commons.logging.LogFactory ;
import java.io.FileNotFoundException ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Map ;
@ -74,6 +78,8 @@ import java.util.regex.Pattern;
public class NoteEditActivity extends Activity implements OnClickListener ,
NoteSettingChangedListener , OnTextViewChangeListener {
private static final org . apache . commons . logging . Log log = LogFactory . getLog ( NoteEditActivity . class ) ;
private class HeadViewHolder {
public TextView tvModified ;
@ -149,7 +155,21 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private String mUserQuery ;
private Pattern mPattern ;
// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// this.setContentView(R.layout.note_edit);
// if (savedInstanceState == null && !initActivityState(getIntent())) {
// finish();
// return;
// }
// initResources();
// }
private final int PHOTO_REQUEST = 1 ;
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
this . setContentView ( R . layout . note_edit ) ;
@ -159,6 +179,23 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return ;
}
initResources ( ) ;
//根据id获取添加图片按钮
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" ) ;
//ACTION_GET_CONTENT: 允许用户选择特殊种类的数据,并返回(特殊种类的数据:照一张相片或录一段音)
Intent loadImage = new Intent ( Intent . ACTION_GET_CONTENT ) ;
//Category属性用于指定当前动作( Action) 被执行的环境.
//CATEGORY_OPENABLE; 用来指示一个ACTION_GET_CONTENT的intent
loadImage . addCategory ( Intent . CATEGORY_OPENABLE ) ;
loadImage . setType ( "image/*" ) ;
startActivityForResult ( loadImage , PHOTO_REQUEST ) ;
}
} ) ;
}
/ * *
@ -275,6 +312,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
switchToListMode ( mWorkingNote . getContent ( ) ) ;
} else {
mNoteEditor . setText ( getHighlightQueryResult ( mWorkingNote . getContent ( ) , mUserQuery ) ) ;
convertToImage ( ) ;
mNoteEditor . setSelection ( mNoteEditor . getText ( ) . length ( ) ) ;
}
for ( Integer id : sBgSelectorSelectionMap . keySet ( ) ) {
@ -293,6 +331,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* is not ready
* /
showAlertHeader ( ) ;
convertToImage ( ) ; // 确保每次进入页面都执行图片转换
}
private void showAlertHeader ( ) {
@ -312,6 +351,71 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} ;
}
private void convertToImage ( ) {
if ( ContextCompat . checkSelfPermission ( this , Manifest . permission . READ_EXTERNAL_STORAGE )
! = PackageManager . PERMISSION_GRANTED ) {
Log . e ( TAG , "缺少读取外部存储权限,无法解码图片" ) ;
return ;
}
NoteEditText noteEditText = ( NoteEditText ) findViewById ( R . id . note_edit_view ) ;
Editable editable = noteEditText . getText ( ) ;
String noteText = editable . toString ( ) ;
// 使用正则表达式匹配图片路径
Pattern pattern = Pattern . compile ( "\\[local\\](.*?)\\[/local\\]" ) ;
Matcher matcher = pattern . matcher ( noteText ) ;
// 存储匹配到的图片路径及其位置
java . util . List < java . util . Map < String , Object > > matches = new java . util . ArrayList < > ( ) ;
while ( matcher . find ( ) ) {
String path = matcher . group ( 1 ) ;
java . util . Map < String , Object > matchInfo = new java . util . HashMap < > ( ) ;
matchInfo . put ( "path" , path ) ;
matchInfo . put ( "start" , matcher . start ( ) ) ;
matchInfo . put ( "end" , matcher . end ( ) ) ;
matches . add ( matchInfo ) ;
}
for ( int i = matches . size ( ) - 1 ; i > = 0 ; i - - ) {
java . util . Map < String , Object > matchInfo = matches . get ( i ) ;
String path = ( String ) matchInfo . get ( "path" ) ;
int start = ( int ) matchInfo . get ( "start" ) ;
int end = ( int ) matchInfo . get ( "end" ) ;
Bitmap bitmap = null ;
Log . d ( TAG , "尝试解码图片,路径:" + path ) ;
java . io . File file = new java . io . File ( path ) ;
if ( ! file . exists ( ) ) {
Log . e ( TAG , "图片文件不存在,路径:" + path ) ;
continue ;
}
try {
bitmap = BitmapFactory . decodeFile ( path ) ;
if ( bitmap ! = null ) {
Log . d ( TAG , "图片解码成功,路径:" + path ) ;
} else {
Log . e ( TAG , "图片解码失败,路径:" + path ) ;
}
} catch ( Exception e ) {
Log . e ( TAG , "图片解码异常,路径:" + path , e ) ;
}
if ( bitmap ! = null ) {
ImageSpan imageSpan = new ImageSpan ( this , bitmap ) ;
String imgTag = "[local]" + path + "[/local]" ;
SpannableString spannableString = new SpannableString ( imgTag ) ;
spannableString . setSpan ( imageSpan , 0 , imgTag . length ( ) ,
Spannable . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
// 替换原始文本
editable . replace ( start , end , spannableString ) ;
}
}
}
@Override
protected void onNewIntent ( Intent intent ) {
super . onNewIntent ( intent ) ;
@ -837,6 +941,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteEditor . setText ( getHighlightQueryResult ( mWorkingNote . getContent ( ) , mUserQuery ) ) ;
mEditTextList . setVisibility ( View . GONE ) ;
mNoteEditor . setVisibility ( View . VISIBLE ) ;
convertToImage ( ) ;
}
}
@ -865,6 +970,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private boolean saveNote ( ) {
getWorkingText ( ) ;
// 确保在保存前转换所有图片路径为可显示的格式
convertToImage ( ) ;
boolean saved = mWorkingNote . saveNote ( ) ;
if ( saved ) {
/ * *
@ -928,4 +1035,145 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private void showToast ( int resId , int duration ) {
Toast . makeText ( this , resId , duration ) . show ( ) ;
}
//获取文件的real path
public String getPath ( final Context context , final Uri uri ) {
final boolean isKitKat = Build . VERSION . SDK_INT > = Build . VERSION_CODES . KITKAT ;
// DocumentProvider
if ( isKitKat & & DocumentsContract . isDocumentUri ( context , uri ) ) {
// ExternalStorageProvider
// if (isExternalStorageDocument(uri)) {
// final String docId = DocumentsContract.getDocumentId(uri);
// final String[] split = docId.split(":");
// final String type = split[0];
//
// if ("primary".equalsIgnoreCase(type)) {
// return Environment.getExternalStorageDirectory() + "/" + split[1];
// }
// }
// // DownloadsProvider
// else if (isDownloadsDocument(uri)) {
// final String id = DocumentsContract.getDocumentId(uri);
// final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
// return getDataColumn(context, contentUri, null, null);
// }
// MediaProvider
// else
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 ;
}
//获取数据列_获取此 Uri 的数据列的值。这对MediaStore Uris 和其他基于文件的 ContentProvider。
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 isExternalStorageDocument(Uri uri) {
// return "com.android.externalstorage.documents".equals(uri.getAuthority());
// }
//
// //是否为下载文件
// public boolean isDownloadsDocument(Uri uri) {
// return "com.android.providers.downloads.documents".equals(uri.getAuthority());
// }
//是否为媒体文件
public boolean isMediaDocument ( Uri uri ) {
return "com.android.providers.media.documents" . equals ( uri . getAuthority ( ) ) ;
}
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 ( ) ; //1.获得图片的真实路径
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 ) {
//3.根据Bitmap对象创建ImageSpan对象
Log . d ( TAG , "onActivityResult: bitmap is not null" ) ;
ImageSpan imageSpan = new ImageSpan ( NoteEditActivity . this , bitmap ) ;
String path = getPath ( this , originalUri ) ;
//4.使用[local][/local]将path括起来, 用于之后方便识别图片路径在note中的位置
String img_fragment = "[local]" + path + "[/local]" ;
//创建一个SpannableString对象, 以便插入用ImageSpan对象封装的图像
SpannableString spannableString = new SpannableString ( img_fragment ) ;
spannableString . setSpan ( imageSpan , 0 , img_fragment . length ( ) , Spannable . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
//5.将选择的图片追加到EditText中光标所在位置
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 . mContent = e . getText ( ) . toString ( ) ;
//6.把改动提交到数据库中,两个数据库表都要改的
ContentResolver contentResolver = getContentResolver ( ) ;
ContentValues contentValues = new ContentValues ( ) ;
final long id = mWorkingNote . getNoteId ( ) ;
contentValues . put ( "snippet" , mWorkingNote . mContent ) ;
contentResolver . update ( Uri . parse ( "content://micode_notes/note" ) , contentValues , "_id=?" , new String [ ] { "" + id } ) ;
ContentValues contentValues1 = new ContentValues ( ) ;
contentValues1 . put ( "content" , mWorkingNote . mContent ) ;
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 ;
}
}
//
}