@ -16,25 +16,45 @@
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.ContentResolver ;
import android.content.ContentUris ;
import android.content.Context ;
import android.content.DialogInterface ;
import android.content.Intent ;
import android.content.SharedPreferences ;
import android.content.pm.PackageManager ;
import android.database.Cursor ;
import android.graphics.Bitmap ;
import android.graphics.BitmapFactory ;
import android.graphics.Paint ;
import android.media.MediaPlayer ;
import android.net.Uri ;
import android.os.Build ;
import android.os.Bundle ;
import android.os.Environment ;
import android.preference.PreferenceManager ;
import android.provider.DocumentsContract ;
import android.provider.MediaStore ;
import android.provider.OpenableColumns ;
import android.text.Editable ;
import android.text.Layout ;
import android.text.Spannable ;
import android.text.SpannableString ;
import android.text.SpannableStringBuilder ;
import android.text.TextUtils ;
import android.text.format.DateUtils ;
import android.text.style.AlignmentSpan ;
import android.text.style.BackgroundColorSpan ;
import android.text.style.ClickableSpan ;
import android.text.style.ImageSpan ;
import android.text.style.URLSpan ;
import android.util.Log ;
import android.view.LayoutInflater ;
import android.view.Menu ;
@ -42,11 +62,14 @@ import android.view.MenuItem;
import android.view.MotionEvent ;
import android.view.View ;
import android.view.View.OnClickListener ;
import android.view.ViewGroup ;
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,6 +88,8 @@ 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.io.IOException ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Map ;
@ -74,6 +99,8 @@ import java.util.regex.Pattern;
public class NoteEditActivity extends Activity implements OnClickListener ,
NoteSettingChangedListener , OnTextViewChangeListener {
private static final int PHOTO_REQUEST = 1 ;
private class HeadViewHolder {
public TextView tvModified ;
@ -119,7 +146,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private static final String TAG = "NoteEditActivity" ;
private static final int REQUEST_IMAGE_SELECT = 1 ;
private static final int RECORD_REQUEST = 1987 ;
private Uri selectedAudioUri ;
private HeadViewHolder mNoteHeaderHolder ;
private View mHeadViewPanel ;
@ -140,12 +170,16 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private static final String PREFERENCE_FONT_SIZE = "pref_font_size" ;
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10 ;
private static final int REQUEST_AUDIO_SELECT = 2 ;
public static final String TAG_CHECKED = String . valueOf ( '\u221A' ) ;
public static final String TAG_UNCHECKED = String . valueOf ( '\u25A1' ) ;
private LinearLayout mEditTextList ;
private LinearLayout audioContainer ;
private MediaPlayer mediaPlayer ;
private String mUserQuery ;
private Pattern mPattern ;
@ -158,6 +192,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
finish ( ) ;
return ;
}
audioContainer = findViewById ( R . id . audioContainer ) ;
initResources ( ) ;
}
@ -293,8 +330,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* is not ready
* /
showAlertHeader ( ) ;
convertToImage ( ) ;
}
private void showAlertHeader ( ) {
if ( mWorkingNote . hasClockAlert ( ) ) {
long time = System . currentTimeMillis ( ) ;
@ -312,6 +352,97 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} ;
}
private void convertToImage ( ) {
NoteEditText noteEditText = findViewById ( R . id . note_edit_view ) ;
Editable editable = noteEditText . getEditableText ( ) ;
String noteText = editable . toString ( ) ;
int length = editable . length ( ) ;
int cursorPositionBeforeInsert = noteEditText . getSelectionStart ( ) ;
// 在光标位置插入换行符
noteEditText . setSelection ( length ) ;
boolean inserted = false ;
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 = BitmapFactory . decodeFile ( path ) ;
if ( bitmap ! = null ) {
int desiredHeight = 2000 ;
int originalWidth = bitmap . getWidth ( ) ;
int originalHeight = bitmap . getHeight ( ) ;
int desiredWidth = ( originalWidth * desiredHeight ) / originalHeight ;
Bitmap scaledBitmap = Bitmap . createScaledBitmap ( bitmap , desiredWidth , desiredHeight , false ) ;
ImageSpan imageSpan = new ImageSpan ( this , scaledBitmap ) ;
String ss = "[local]" + path + "[/local]" ;
SpannableString spannableString = new SpannableString ( ss ) ;
spannableString . setSpan ( new AlignmentSpan . Standard ( Layout . Alignment . ALIGN_CENTER ) , 0 , ss . length ( ) , Spannable . SPAN_EXCLUSIVE_EXCLUSIVE ) ; // 设置居中对齐
spannableString . setSpan ( imageSpan , 0 , ss . length ( ) , Spannable . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
// 添加ClickableSpan, 使得点击图片末尾的光标可以和图片对齐
ClickableSpan clickableSpan = new ClickableSpan ( ) {
@Override
public void onClick ( View view ) {
int selectionEnd = noteEditText . getSelectionEnd ( ) ;
if ( selectionEnd = = cursorPositionBeforeInsert ) {
noteEditText . setSelection ( cursorPositionBeforeInsert + 1 ) ;
}
}
} ;
spannableString . setSpan ( clickableSpan , ss . length ( ) , ss . length ( ) , Spannable . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
editable . replace ( i , i + len + 15 , spannableString ) ;
inserted = true ;
noteEditText . setSelection ( cursorPositionBeforeInsert ) ;
ClickableSpan [ ] spans = editable . getSpans ( cursorPositionBeforeInsert , cursorPositionBeforeInsert , ClickableSpan . class ) ;
if ( spans ! = null & & spans . length > 0 ) {
for ( ClickableSpan span : spans ) {
editable . removeSpan ( span ) ;
}
}
}
}
}
}
if ( inserted ) {
editable . append ( "\n" ) ;
// 在插入图片后为NoteEditText设置触摸事件监听器
noteEditText . setOnTouchListener ( new View . OnTouchListener ( ) {
@Override
public boolean onTouch ( View v , MotionEvent event ) {
int action = event . getAction ( ) ;
if ( action = = MotionEvent . ACTION_DOWN ) {
int offset = noteEditText . getOffsetForPosition ( event . getX ( ) , event . getY ( ) ) ;
ImageSpan [ ] imageSpans = editable . getSpans ( 0 , editable . length ( ) , ImageSpan . class ) ;
for ( ImageSpan span : imageSpans ) {
int start = editable . getSpanStart ( span ) ;
int end = editable . getSpanEnd ( span ) ;
if ( offset > = start & & offset < = end ) {
// 如果光标位于图片所在行, 返回true, 表示消费了该事件
return true ;
}
}
}
return false ;
}
} ) ;
}
}
@Override
protected void onNewIntent ( Intent intent ) {
super . onNewIntent ( intent ) ;
@ -547,12 +678,257 @@ public class NoteEditActivity extends Activity implements OnClickListener,
case R . id . menu_delete_remind :
mWorkingNote . setAlertDate ( 0 , false ) ;
break ;
case R . id . menu_insert_image :
Intent loadImage = new Intent ( Intent . ACTION_GET_CONTENT ) ;
loadImage . addCategory ( Intent . CATEGORY_OPENABLE ) ;
loadImage . setType ( "image/*" ) ;
startActivityForResult ( loadImage , PHOTO_REQUEST ) ;
break ;
// Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// startActivityForResult(intent, REQUEST_IMAGE_SELECT);
case R . id . menu_insert_audio :
Intent intent = new Intent ( Intent . ACTION_GET_CONTENT ) ;
intent . setType ( "audio/*" ) ;
startActivityForResult ( intent , REQUEST_AUDIO_SELECT ) ;
// openAudioSelector();
break ;
default :
break ;
}
return true ;
}
// private void openImageSelector() {
// Intent intent = new Intent(Intent.ACTION_PICK);
// intent.setType("image/*");
// startActivityForResult(intent, REQUEST_IMAGE_SELECT);
// }
@Override
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.获得图片的真实路径
addImage ( resolver , originalUri ) ;
break ;
case REQUEST_AUDIO_SELECT :
selectedAudioUri = intent . getData ( ) ;
String fileName = getFileName ( selectedAudioUri ) ;
addAudioComponent ( fileName , selectedAudioUri ) ;
break ;
default :
break ;
}
}
private String getFileName ( Uri uri ) {
String result = null ;
if ( uri . getScheme ( ) . equals ( "content" ) ) {
try ( Cursor cursor = getContentResolver ( ) . query ( uri , null , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
result = cursor . getString ( cursor . getColumnIndex ( OpenableColumns . DISPLAY_NAME ) ) ;
}
}
}
if ( result = = null ) {
result = uri . getPath ( ) ;
int cut = result . lastIndexOf ( '/' ) ;
if ( cut ! = - 1 ) {
result = result . substring ( cut + 1 ) ;
}
}
return result ;
}
private void addImage ( ContentResolver resolver , Uri originalUri ) {
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 ) ; //将图片插入到光标所在位置
//
// // 添加以下代码来在图片插入后自动换行
// edit_text.insert(index + spannableString.length(), "\n");// 在图片后添加一个换行符
//
// // 更新光标
// e.setSelection(index + spannableString.length() + 1); // 将光标移动到换行符后面
mWorkingNote . mContent = e . getText ( ) . toString ( ) ;
mWorkingNote . mContent = mWorkingNote . mContent . replaceAll ( "(?m)^[ \t]*\r?\n" , "" ) ; // 删除空白行
// //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 ( ) ;
}
}
private void addAudioComponent ( String fileName , final Uri uri ) {
LinearLayout audioLayout = new LinearLayout ( this ) ;
audioLayout . setOrientation ( LinearLayout . HORIZONTAL ) ;
audioLayout . setLayoutParams ( new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT , ViewGroup . LayoutParams . WRAP_CONTENT ) ) ;
audioLayout . setPadding ( 0 , 16 , 0 , 16 ) ;
TextView audioFileName = new TextView ( this ) ;
audioFileName . setText ( fileName ) ;
audioFileName . setLayoutParams ( new LinearLayout . LayoutParams (
0 , ViewGroup . LayoutParams . WRAP_CONTENT , 1.0f ) ) ;
Button playAudioButton = new Button ( this ) ;
playAudioButton . setText ( "Play" ) ;
playAudioButton . setLayoutParams ( new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . WRAP_CONTENT , ViewGroup . LayoutParams . WRAP_CONTENT ) ) ;
playAudioButton . setOnClickListener ( new View . OnClickListener ( ) {
@Override
public void onClick ( View v ) {
playAudio ( uri ) ;
}
} ) ;
audioLayout . addView ( audioFileName ) ;
audioLayout . addView ( playAudioButton ) ;
audioContainer . addView ( audioLayout ) ;
}
private void playAudio ( Uri uri ) {
if ( mediaPlayer ! = null ) {
mediaPlayer . release ( ) ;
}
mediaPlayer = MediaPlayer . create ( this , uri ) ;
mediaPlayer . start ( ) ;
}
@Override
protected void onDestroy ( ) {
super . onDestroy ( ) ;
if ( mediaPlayer ! = null ) {
mediaPlayer . release ( ) ;
mediaPlayer = null ;
}
}
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 ( ) ) ;
}
private void setReminder ( ) {
DateTimePickerDialog d = new DateTimePickerDialog ( this , System . currentTimeMillis ( ) ) ;
d . setOnDateTimeSetListener ( new OnDateTimeSetListener ( ) {
@ -725,6 +1101,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return spannable ;
}
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 ) ;
@ -870,4 +1247,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private void showToast ( int resId , int duration ) {
Toast . makeText ( this , resId , duration ) . show ( ) ;
}
}