diff --git a/.gitignore b/.gitignore index e3879fd..8b82a29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /src/Notes/ /src/.idea/ -/src/Notes-master1/ +/src/Notes/ /.idea/ diff --git a/doc/小米便签质量分析报告.docx b/doc/小米便签质量分析报告.docx index 4d17085..f4c7061 100644 Binary files a/doc/小米便签质量分析报告.docx and b/doc/小米便签质量分析报告.docx differ diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index b6036d7..3b7012e 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,25 +1,56 @@ + + + + + android:versionName="0.1" > + + - - - - - - - + + + + + + + + + + + + - + + + + + + + + android:icon="@drawable/icon_app" + android:label="@string/app_name" + android:requestLegacyExternalStorage="true"> - + + android:windowSoftInputMode="adjustPan" > + + + + + - + + android:theme="@style/NoteTheme" > - + - - + - + - - + - + - - + - + + + + android:multiprocess="true" /> + + + android:label="@string/app_widget2x2" > - + - + + android:label="@string/app_widget4x4" > - + - + - - + + + + - + + + + android:name="net.micode.notes.ui.AlarmReceiver" + android:process=":remote" > + + + + android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" > + + + + android:theme="@android:style/Theme.Holo.Light" > + + + + android:name="net.micode.notes.gtask.remote.GTaskSyncService" + android:exported="false" > + + + - - \ No newline at end of file + diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 0ad5f86..b740b4a 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -38,6 +38,8 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import androidx.core.app.ActivityCompat; + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.TextNote; @@ -114,6 +116,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); } + public static final int PHOTO_REQUEST = 1;//添加图片 + private static final int REQUEST_EXTERNAL_STORAGE=1;//权限申请 + private static String[] PERMISSIONS_STORAGE = { + "android.permission.READ_EXTERNAL_STORAGE", + "android.permission.WRITE_EXTERNAL_STORAGE" };//权限名称 + private static final String TAG = "NoteEditActivity"; private HeadViewHolder mNoteHeaderHolder; private View mHeadViewPanel; @@ -136,7 +144,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, private String mUserQuery; private Pattern mPattern; - + /** * @method Textchange * @description 对原始文本进行处理,去除图片字符、换行符和空格字符 @@ -212,7 +220,23 @@ public class NoteEditActivity extends Activity implements OnClickListener, return; } initResources(); - count(); + + final ImageButton add_img_btn = (ImageButton) findViewById(R.id.add_img_btn);//根据id获取添加图片按钮 + //为点击图片按钮设置监听器 + 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); + } + }); + convertToImage();//将路径显示为图片 } /** @@ -356,6 +380,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, * is not ready */ showAlertHeader(); + convertToImage();//将路径转化未图片 } /** * @method showAlertHeader @@ -1050,4 +1075,221 @@ public class NoteEditActivity extends Activity implements OnClickListener, private void showToast(int resId, int duration) { Toast.makeText(this, resId, duration).show(); } + + /** + * @method convertToImage + * @description: 路径字符串格式 转换为 图片image格式 + * @date: 2024/1/3 19:19 + * @author: WuShuxian + * @param: void + * @return: void + */ + private void convertToImage() { + NoteEditText noteEditText = (NoteEditText) findViewById(R.id.note_edit_view); //获取当前的edit + Editable editable = noteEditText.getText();//1.获取text + String noteText = editable.toString(); //2.将note内容转换为字符串 + int length = editable.length(); //内容的长度 + //3.截取img片段 [local]+uri+[/local],提取uri。算法效率有待改善可以考虑KMP + for(int i = 0; i < length; i++) { + for(int j = i; j < length; j++) { + String img_fragment = noteText.substring(i, j+1); //img_fragment:关于图片路径的片段 + if(img_fragment.length() > 15 && img_fragment.endsWith("[/local]") && + img_fragment.startsWith("[local]")){ + int limit = 7; //[local]为7个字符 + int len = img_fragment.length()-15;//[local][/local]共15个字符,剩下的为真正的path长度 + String path = img_fragment.substring(limit,limit+len);//获取到了图片路径从[local]之后的len个字符就是path + 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);//4.创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像 + + spannableString.setSpan(imageSpan, 0, ss.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//5.将指定的标记对象附加到文本的开始...结束范围 + Log.d(TAG, "Create spannable string success!"); + Editable edit_text = noteEditText.getEditableText(); + edit_text.delete(i,i+len+15); //6.删掉图片路径的文字 + edit_text.insert(i, spannableString); //7.在路径的起始位置插入图片 + } + } + } + } + } + + /** + * @method onActivityResult + * @description: 插入图片时:重写onActivityResult()来处理返回的数据,点击插入图片返回后执行 + * @date: 2024/1/3 19:20 + * @author: WuShuxian + * @param: + * @return: void + */ + @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.获得图片的真实路径 + Bitmap bitmap = null; + try { + bitmap = BitmapFactory.decodeStream(resolver.openInputStream(originalUri));//2.解码图片 + } catch (FileNotFoundException e) { + Log.d(TAG, "onActivityResult: get file_exception"); + e.printStackTrace(); + } + //3.根据Bitmap对象创建ImageSpan对象 + 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]";//4.使用[local][/local]将path括起来,用于之后方便识别图片路径在note中的位置 + SpannableString spannableString = new SpannableString(img_fragment);//创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像 + spannableString.setSpan(imageSpan, 0, + img_fragment.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + NoteEditText e = (NoteEditText) findViewById(R.id.note_edit_view);//5.将选择的图片追加到EditText中光标所在位置 + int index = e.getSelectionStart(); //获取光标所在位置 + Log.d(TAG, "Index是: " + index); + Editable edit_text = e.getEditableText(); + edit_text.insert(index, spannableString); //将图片插入到光标所在位置 + + mWorkingNote.setWorkingText(e.getText().toString()); + //6.把改动提交到数据库中,两个数据库表都要改的 + 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; + } + } + + /** + * @method getPath + * @description: 获取文件的real path + * @date: 2024/1/3 19:22 + * @author: WuShuxian + * @param: + * @return: + */ + 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)) { + 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; + } + + /** + * @method isMediaDocument + * @description: 判断是否为媒体文件 + * @date: 2024/1/3 19:23 + * @author: WuShuxian + * @param: uri + * @return: + */ + public boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * @method getDataColumn + * @description: 获取数据列_获取此 Uri 的数据列的值。这对MediaStore Uris 和其他基于文件的 ContentProvider。 + * @date: 2024/1/3 19:23 + * @author: WuShuxian + * @param: + * @return: + */ + 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; + } + + /** + * @method checkStoragePermissions + * @description: 用于动态申请权限 + * @date: 2024/1/6 8:58 + * @author: WuShuxian + * @param: activity + * @return: void + */ + public static void checkStoragePermissions(Activity activity){ + try{ + //监测是否有写/读的权限 + int permission= ActivityCompat.checkSelfPermission(activity, + "android.permission.WRITE_EXTERNAL_STORAGE"); + int permission1= ActivityCompat.checkSelfPermission(activity, + "android.permission.READ_EXTERNAL_STORAGE"); + if(permission != PackageManager.PERMISSION_GRANTED){ + //没有写的权限,去申请写的权限,或弹出对话框 + ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE); + } + if(permission1 != PackageManager.PERMISSION_GRANTED){ + //没有读的权限,去申请读的权限,或弹出对话框 + ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE); + } + + }catch (Exception e){ + e.printStackTrace(); + } + } } diff --git a/src/main/res/drawable-hdpi/ic_menu_gallery_new.png b/src/main/res/drawable-hdpi/ic_menu_gallery_new.png new file mode 100644 index 0000000..863c493 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_menu_gallery_new.png differ diff --git a/src/main/res/layout/note_edit.xml b/src/main/res/layout/note_edit.xml index 59415d7..defd4eb 100644 --- a/src/main/res/layout/note_edit.xml +++ b/src/main/res/layout/note_edit.xml @@ -405,4 +405,13 @@ android:src="@drawable/selected" /> + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 469b04c..dccf596 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -39,6 +39,7 @@ notes_%s.txt (%d) + Add picture New Folder Export text Sync