merge #7

Merged
p4vbyojna merged 5 commits from ClearDewy into master 3 years ago

1
.gitignore vendored

@ -2,3 +2,4 @@
/.idea/
/app/build/
/gradle/wrapper/gradle-wrapper.jar
/local.properties

@ -6,9 +6,9 @@ android {
defaultConfig {
applicationId "net.micode.notes"
minSdkVersion 19
minSdkVersion 33
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 30
targetSdkVersion 33
}
buildTypes {

@ -23,7 +23,6 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
@ -33,10 +32,27 @@
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Required only if your app targets Android 13. -->
<!-- Declare one or more the following permissions only if your app needs
to access data that's protected by them. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Required to maintain app compatibility. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application
android:icon="@drawable/icon_app"
android:theme="@style/AppTheme.Base"
android:label="@string/app_name" >
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
>
<activity
android:exported="true"
android:name=".ui.NotesListActivity"

@ -38,7 +38,7 @@ public class WorkingNote {
// Note Id
private long mNoteId;
// Note content
private String mContent;
public String mContent;
// Note mode
private int mMode;
@ -227,12 +227,8 @@ public class WorkingNote {
}
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
return !mIsDeleted && (existInDatabase() || !TextUtils.isEmpty(mContent))
&& (!existInDatabase() || mNote.isLocalModified());
}
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
@ -316,7 +312,7 @@ public class WorkingNote {
}
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
return mAlertDate > 0;
}
public String getContent() {
@ -394,4 +390,5 @@ public class WorkingNote {
*/
void onCheckListModeChanged(int oldMode, int newMode);
}
}

@ -64,9 +64,7 @@ public class DataUtils {
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;

@ -64,7 +64,7 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
Intent intent = getIntent();
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mNoteId = Long.parseLong(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)

@ -16,25 +16,36 @@
package net.micode.notes.ui;
import android.app.Activity;
import android.Manifest;
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.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
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;
@ -47,11 +58,16 @@ 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 androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import net.micode.notes.R;
@ -67,6 +83,7 @@ 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.Map;
@ -154,6 +171,9 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
private String mUserQuery;
private Pattern mPattern;
private ImageButton mImageButton; // 添加图片按钮
private ActivityResultLauncher<String> requestPermissionLauncher; //获取存储权限
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -300,8 +320,12 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
* is not ready
*/
showAlertHeader();
//将有图片路径的位置转换为图片
requestPermissionLauncher.launch(Manifest.permission.READ_MEDIA_IMAGES);
}
private void showAlertHeader() {
if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis();
@ -316,7 +340,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
} else {
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
};
}
if(mWorkingNote.getIsLocked()){
mNoteHeaderHolder.ivUnlocked.setVisibility(View.GONE);
mNoteHeaderHolder.ivLocked.setVisibility(View.VISIBLE);
@ -369,13 +393,10 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
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;
return !(ev.getX() < x)
&& !(ev.getX() > (x + view.getWidth()))
&& !(ev.getY() < y)
&& !(ev.getY() > (y + view.getHeight()));
}
private void initResources() {
@ -402,7 +423,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
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);
/**
@ -414,6 +435,13 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
// 获取读取存储权限
requestPermissionLauncher=getrequestDataLauncher();
// 初始化添加图片按钮
mImageButton = (ImageButton) findViewById(R.id.add_img_btn);
ActivityResultLauncher<Intent> intentActivityResultLauncher=getActivityResultLauncher();
mImageButton.setOnClickListener(view -> onAddImage(intentActivityResultLauncher));
}
@Override
@ -931,5 +959,233 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListe
Toast.makeText(this, resId, duration).show();
}
/**
* @author: ClearDewy
* @date: 2023/3/9 11:20
* @param: [android.content.Context, android.net.Uri]
* @return: java.lang.String
* @description:Url
**/
public static String getPathFromUri(final Context context, final Uri uri) {
if (uri == null) {
return null;
}
// 判斷是否為Android 4.4之後的版本
if (DocumentsContract.isDocumentUri(context, uri)) {
// 如果是Android 4.4之後的版本而且屬於文件URI
final String authority = uri.getAuthority();
// 判斷Authority是否為本地端檔案所使用的
if ("com.android.externalstorage.documents".equals(authority)) {
// 外部儲存空間
final String docId = DocumentsContract.getDocumentId(uri);
final String[] divide = docId.split(":");
final String type = divide[0];
if ("primary".equals(type)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/").concat(divide[1]);
return path;
} else {
String path = "/storage/".concat(type).concat("/").concat(divide[1]);
return path;
}
} else if ("com.android.providers.downloads.documents".equals(authority)) {
// 下載目錄
final String docId = DocumentsContract.getDocumentId(uri);
if (docId.startsWith("raw:")) {
final String path = docId.replaceFirst("raw:", "");
return path;
}
final Uri downloadUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId));
String path = queryAbsolutePath(context, downloadUri);
return path;
} else if ("com.android.providers.media.documents".equals(authority)) {
// 圖片、影音檔案
final String docId = DocumentsContract.getDocumentId(uri);
final String[] divide = docId.split(":");
final String type = divide[0];
Uri mediaUri = null;
if ("image".equals(type)) {
mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
mediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
return null;
}
mediaUri = ContentUris.withAppendedId(mediaUri, Long.parseLong(divide[1]));
String path = queryAbsolutePath(context, mediaUri);
return path;
}
} else {
// 如果是一般的URI
final String scheme = uri.getScheme();
String path = null;
if ("content".equals(scheme)) {
// 內容URI
path = queryAbsolutePath(context, uri);
} else if ("file".equals(scheme)) {
// 檔案URI
path = uri.getPath();
}
return path;
}
return null;
}
public static String queryAbsolutePath(final Context context, final Uri uri) {
final String[] projection = {MediaStore.MediaColumns.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
return cursor.getString(index);
}
} catch (final Exception ex) {
ex.printStackTrace();
if (cursor != null) {
cursor.close();
}
}
return null;
}
private ActivityResultLauncher<Intent> getActivityResultLauncher(){
return registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>(){
// 重写回调方法
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() != RESULT_OK) return;
ContentResolver resolver = getContentResolver();
Uri originalUri = result.getData().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 = getPathFromUri(NoteEditActivity.this, originalUri);
//4.使用[local][/local]将path括起来用于之后方便识别图片路径在note中的位置
String img_fragment = "<img>" + path + "</img>";
//创建一个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});
}
}
});
}
/**
* @author: ClearDewy
* @date: 2023/3/10 12:43
* @param: []
* @return: androidx.activity.result.ActivityResultLauncher<java.lang.String>
* @description:API>=33
**/
private ActivityResultLauncher<String> getrequestDataLauncher(){
return registerForActivityResult(new ActivityResultContracts.RequestPermission(), result -> {
if (result){
Log.d(TAG,"用户已授权");
convertToImage();
}else{
Log.d(TAG,"用户取消授权");
}
});
}
//路径字符串格式 转换为 图片image格式
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
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个字符
//[local][/local]共15个字符剩下的为真正的path长度
int len = img_fragment.length()-15;
//从[local]之后的len个字符就是path
String path = img_fragment.substring(limit,limit+len);//获取到了图片路径
}
}
}
for (int i = 0; i < length; i++) {
if (noteText.startsWith("<img>", i)){
for(int j=i+5;j< length;j++){
if (noteText.substring(i,j+1).endsWith("</img>")){
Bitmap bitmap = null;
String path=noteText.substring(i+5,j-5);
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);
//4.创建一个SpannableString对象以便插入用ImageSpan对象封装的图像
String ss = "<img>" + path + "</img>";
SpannableString spannableString = new SpannableString(ss);
//5.将指定的标记对象附加到文本的开始...结束范围
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,j-i+1); //6.删掉图片路径的文字
edit_text.insert(i, spannableString); //7.在路径的起始位置插入图片
i=j;break;
}
}
}
}
}
}
// 添加图片按钮点击时触发
private void onAddImage(ActivityResultLauncher<Intent> intentActivityResultLauncher){
Intent imgIntent=new Intent(Intent.ACTION_GET_CONTENT);
//Category属性用于指定当前动作Action被执行的环境.
//CATEGORY_OPENABLE; 用来指示一个ACTION_GET_CONTENT的intent
imgIntent.addCategory(Intent.CATEGORY_OPENABLE);
imgIntent.setType("image/*");
intentActivityResultLauncher.launch(imgIntent);
}
}

@ -72,19 +72,13 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.FingerprintDialogFragment;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyStore;
import java.util.HashSet;
@ -705,20 +699,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_new_note:
if(mCurrentFolderId==Notes.ID_TRASH_FOLER){
Toast.makeText(
NotesListActivity.this,
R.string.forbidden_add_in_trash,
Toast.LENGTH_SHORT).show();
}else {
createNewNote();
}
break;
default:
break;
}
createNewNote();
}
private void showSoftInput() {

@ -101,7 +101,8 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_height="fill_parent"
android:orientation="horizontal">
<net.micode.notes.ui.NoteEditText
android:id="@+id/note_edit_view"
@ -413,4 +414,13 @@
android:src="@drawable/selected" />
</FrameLayout>
</LinearLayout>
<ImageButton
android:id="@+id/add_img_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="7dp"
android:layout_gravity="bottom"
android:layout_marginBottom="20dp"
android:src="@android:drawable/ic_menu_gallery" />
</FrameLayout>

Loading…
Cancel
Save