diff --git a/Src/app/src/main/AndroidManifest.xml b/Src/app/src/main/AndroidManifest.xml
index 3915f9f..bc713e4 100644
--- a/Src/app/src/main/AndroidManifest.xml
+++ b/Src/app/src/main/AndroidManifest.xml
@@ -6,6 +6,8 @@
android:versionName="0.1">
+
+
diff --git a/Src/app/src/main/java/net/micode/notes/data/Notes.java b/Src/app/src/main/java/net/micode/notes/data/Notes.java
index a053dfe..d8c643d 100644
--- a/Src/app/src/main/java/net/micode/notes/data/Notes.java
+++ b/Src/app/src/main/java/net/micode/notes/data/Notes.java
@@ -90,6 +90,8 @@ public class Notes {
public static final String GTASK_ID = "gtask_id"; // Google任务ID (类型: TEXT)
public static final String VERSION = "version"; // 版本号 (类型: INTEGER)
public static final String IMAGE_PATH = "image_path";// 新增列存储图片路径
+ public static final String USER_ID = "user_id";
+ public static final String AUDIO_PATH = "audio_path";
}
// DataColumns接口:定义数据列
diff --git a/Src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/Src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
index 1a1633e..71c8826 100644
--- a/Src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
+++ b/Src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
@@ -20,7 +20,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db";
// 数据库版本号,用于判断数据库结构是否需要升级
- private static final int DB_VERSION = 6;
+ private static final int DB_VERSION = 8;
// 表接口,定义了数据库中两个主要表的表名:NOTE和DATA
public interface TABLE {
@@ -57,6 +57,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + // 文件夹内笔记数量
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + // 笔记摘要
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + // 笔记类型
+ NoteColumns.USER_ID + " INTEGER NOT NULL DEFAULT -1," + // 新增 user_id 字段
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + // 小部件ID
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + // 小部件类型
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + // 同步ID
@@ -64,7 +65,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +// 原始父ID
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + // Google任务ID
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," + // 笔记版本号
- NoteColumns.IMAGE_PATH + " TEXT" + // 新增列存储图片路径
+ NoteColumns.IMAGE_PATH + " TEXT," + // 新增列存储图片路径
+ NoteColumns.AUDIO_PATH + " TEXT" + //新增列存储音频路径
")";
// SQL语句:创建DATA表
@@ -399,9 +401,15 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
// 在版本5中创建用户表
createUserTable(db); // 确保调用创建用户表的方法
}
- if (oldVersion < 6) { // 假设你现在的版本是5
+ if (oldVersion < 6) { // 增加照片路径新列
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.IMAGE_PATH + " TEXT");
}
+ if(oldVersion < 7){
+ db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN "+ NoteColumns.USER_ID + "INTEGER NOT NULL DEFAULT -1");
+ }
+ if(oldVersion < 8){
+ db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN "+ NoteColumns.AUDIO_PATH + " TEXT");
+ }
// 如果需要,重新创建触发器
reCreateNoteTableTriggers(db); // 重建NOTE表的触发器
diff --git a/Src/app/src/main/java/net/micode/notes/data/UserDao.java b/Src/app/src/main/java/net/micode/notes/data/UserDao.java
index b6b86d9..5a80353 100644
--- a/Src/app/src/main/java/net/micode/notes/data/UserDao.java
+++ b/Src/app/src/main/java/net/micode/notes/data/UserDao.java
@@ -17,8 +17,8 @@ public class UserDao {
this.dbHelper = dbHelper;
}
- // 注册用户的方法
- public boolean registerUser(String username, String password) {
+ // 注册用户,并返回用户 ID
+ public long registerUser(String username, String password) {
SQLiteDatabase db = null;
try {
db = dbHelper.getWritableDatabase();
@@ -26,40 +26,58 @@ public class UserDao {
values.put(COLUMN_USERNAME, username);
values.put(COLUMN_PASSWORD, password);
- // 尝试插入新用户
- long result = db.insert(NotesDatabaseHelper.TABLE.USER, null, values);
+ long userId = db.insert(NotesDatabaseHelper.TABLE.USER, null, values);
- // 输出调试信息
- if (result == -1) {
- Log.d("UserDao", "Registration failed for user: " + username);
- return false; // 插入失败
+ if (userId == -1) {
+ Log.d("UserDao", "注册失败,用户名:" + username);
} else {
- Log.d("UserDao", "Successfully registered user: " + username);
- return true; // 插入成功
+ Log.d("UserDao", "注册成功,用户ID:" + userId);
}
+ return userId; // 返回用户ID
} catch (Exception e) {
- Log.e("UserDao", "Error registering user", e);
- return false; // 发生异常
+ Log.e("UserDao", "注册用户时出错", e);
+ return -1; // 插入失败
} finally {
if (db != null) {
- db.close(); // 确保数据库在操作完后关闭
+ db.close(); // 确保数据库关闭
}
}
}
+ // 登录用户,返回用户 ID,失败返回 -1
+ public int loginUser(String username, String password) {
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
+ Cursor cursor = null;
+ try {
+ cursor = db.query(NotesDatabaseHelper.TABLE.USER, new String[]{"id"}, "username=? AND password=?",
+ new String[]{username, password}, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getInt(0); // 返回用户ID
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return -1; // 登录失败
+ }
-
- // 登录用户的方法
- public boolean loginUser(String username, String password) {
+ // 根据用户名获取用户 ID
+ public int getUserIdByUsername(String username) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
- Cursor cursor = null; // 初始化Cursor为空
+ Cursor cursor = null;
try {
- cursor = db.query(NotesDatabaseHelper.TABLE.USER, null, "username=? AND password=?", new String[]{username, password}, null, null, null);
- return cursor.getCount() > 0; // 如果查询结果超过0,表示用户存在
+ cursor = db.query(NotesDatabaseHelper.TABLE.USER, new String[]{"id"}, "username=?",
+ new String[]{username}, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getInt(0); // 返回用户ID
+ }
} finally {
if (cursor != null) {
- cursor.close(); // 确保Cursor在使用完后关闭
+ cursor.close();
}
}
+ return -1; // 用户不存在
}
+
}
\ No newline at end of file
diff --git a/Src/app/src/main/java/net/micode/notes/login/LoginActivity.java b/Src/app/src/main/java/net/micode/notes/login/LoginActivity.java
index 84e2281..a0b4ecf 100644
--- a/Src/app/src/main/java/net/micode/notes/login/LoginActivity.java
+++ b/Src/app/src/main/java/net/micode/notes/login/LoginActivity.java
@@ -1,7 +1,9 @@
package net.micode.notes.login;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@@ -46,19 +48,31 @@ public class LoginActivity extends Activity {
String password = etLoginPassword.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
- showToast("Username or password cannot be empty");
+ showToast("用户名或密码不能为空");
return;
}
- if (userDao.loginUser(username, password)) {
+ int userId = userDao.loginUser(username, password);
+ Log.d("LoginActivity", "Login userId: " + userId);
+ if (userId != -1) { // 登录成功
+ setCurrentUserId(userId); // 保存用户ID到SharedPreferences
+
Intent intent = new Intent(LoginActivity.this, NotesListActivity.class);
startActivity(intent);
finish();
} else {
- showToast("Invalid username or password");
+ showToast("错误的用户名或密码");
}
}
+ // 新增 setCurrentUserId 方法
+ private void setCurrentUserId(int userId) {
+ Log.d("LoginActivity", "Saving userId to SharedPreferences: " + userId);
+ SharedPreferences.Editor editor = getSharedPreferences("user_session", MODE_PRIVATE).edit();
+ editor.putInt("user_id", userId); // 保存当前用户ID
+ editor.apply();
+ }
+
private void navigateToRegister() {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
diff --git a/Src/app/src/main/java/net/micode/notes/login/RegisterActivity.java b/Src/app/src/main/java/net/micode/notes/login/RegisterActivity.java
index e61c2ae..a5abe3c 100644
--- a/Src/app/src/main/java/net/micode/notes/login/RegisterActivity.java
+++ b/Src/app/src/main/java/net/micode/notes/login/RegisterActivity.java
@@ -1,6 +1,7 @@
package net.micode.notes.login;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -13,6 +14,7 @@ import android.app.Activity;
import net.micode.notes.R;
import net.micode.notes.data.NotesDatabaseHelper;
import net.micode.notes.data.UserDao;
+import net.micode.notes.ui.NotesListActivity;
public class RegisterActivity extends Activity {
@@ -44,21 +46,35 @@ public class RegisterActivity extends Activity {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
- // 检查用户名和密码是否为空
if (username.isEmpty() || password.isEmpty()) {
- showToast("Username or password cannot be empty");
+ showToast("用户名或密码不能为空");
return;
}
- // 尝试注册用户
- if (userDao.registerUser(username, password)) {
- showToast("User registered successfully");
- navigateToLogin(); // 注册成功后返回登录界面
+ long userId = userDao.registerUser(username, password);
+ if (userId != -1) { // 注册成功
+ showToast("注册成功");
+ setCurrentUserId((int) userId); // 保存用户ID到SharedPreferences
+ navigateToNotesList(); // 跳转到主界面
} else {
- showToast("Registration failed");
+ showToast("注册失败,用户名可能已存在");
}
}
+ // 新增 setCurrentUserId 方法
+ private void setCurrentUserId(int userId) {
+ SharedPreferences.Editor editor = getSharedPreferences("user_session", MODE_PRIVATE).edit();
+ editor.putInt("user_id", userId); // 保存当前用户ID
+ editor.apply();
+ }
+
+ // 新增 navigateToNotesList 方法
+ private void navigateToNotesList() {
+ Intent intent = new Intent(RegisterActivity.this, NotesListActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
private void navigateToLogin() {
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
startActivity(intent);
diff --git a/Src/app/src/main/java/net/micode/notes/model/Note.java b/Src/app/src/main/java/net/micode/notes/model/Note.java
index ceb150e..1a5f714 100644
--- a/Src/app/src/main/java/net/micode/notes/model/Note.java
+++ b/Src/app/src/main/java/net/micode/notes/model/Note.java
@@ -47,8 +47,7 @@ public class Note {
* @param folderId 文件夹ID,表示新笔记将被添加到的文件夹
* @return 新创建的笔记的ID
*/
- public static synchronized long getNewNoteId(Context context, long folderId) {
- // 在数据库中创建一个新的笔记
+ public static synchronized long getNewNoteId(Context context, long folderId, int userId) {
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime);
@@ -56,6 +55,10 @@ public class Note {
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
values.put(NoteColumns.PARENT_ID, folderId);
+ values.put(NoteColumns.USER_ID, userId); // 设置 user_id
+ values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
+
+
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
@@ -68,6 +71,8 @@ public class Note {
if (noteId == -1) {
throw new IllegalStateException("错误的笔记ID:" + noteId);
}
+ Log.d("Note", "Inserted note for user_id: " + userId + ", noteId: " + noteId);
+
return noteId;
}
diff --git a/Src/app/src/main/java/net/micode/notes/model/WorkingNote.java b/Src/app/src/main/java/net/micode/notes/model/WorkingNote.java
index b983b09..607cb85 100644
--- a/Src/app/src/main/java/net/micode/notes/model/WorkingNote.java
+++ b/Src/app/src/main/java/net/micode/notes/model/WorkingNote.java
@@ -21,6 +21,7 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -177,9 +178,15 @@ public class WorkingNote {
*/
private void loadNote() {
// 使用内容提供者查询数据库,获取与指定笔记ID匹配的笔记信息
+ SharedPreferences prefs = mContext.getSharedPreferences("user_session", Context.MODE_PRIVATE);
+ int userId = prefs.getInt("user_id", -1);
Cursor cursor = mContext.getContentResolver().query(
- ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
- null, null);
+ ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId),
+ NOTE_PROJECTION,
+ NoteColumns.USER_ID + "=?",
+ new String[]{String.valueOf(userId)},
+ null
+ );
if (cursor != null) {
// 如果查询结果不为空,尝试读取数据
@@ -299,10 +306,21 @@ public class WorkingNote {
if (isWorthSaving()) {
// 检查笔记是否已存在于数据库中
if (!existInDatabase()) {
- // 如果笔记不在数据库中,为该笔记生成一个新的ID
- if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
+ // 获取当前登录用户的 user_id
+ SharedPreferences prefs = mContext.getSharedPreferences("user_session", Context.MODE_PRIVATE);
+
+ int userId = prefs.getInt("user_id", -1); // -1 表示未登录
+ Log.d("WorkingNote", "Saving note with userId: " + userId);
+
+ if (userId == -1) {
+ Log.e(TAG, "无法保存笔记,因为用户未登录");
+ return false; // 未登录时不保存
+ }
+
+ // 如果笔记不在数据库中,为该笔记生成一个新的ID,并关联 user_id
+ if ((mNoteId = Note.getNewNoteId(mContext, mFolderId, userId)) == 0) {
// 若生成新ID失败,记录日志并返回 false 表示保存失败
- Log.e(TAG, "Create new note fail with id:" + mNoteId);
+ Log.e(TAG, "创建新便签失败:" + mNoteId);
return false;
}
}
@@ -324,6 +342,7 @@ public class WorkingNote {
}
+
/**
* 检查笔记是否已存在于数据库中。
*
@@ -665,17 +684,6 @@ public class WorkingNote {
}
}
- private String getFileExtension(Uri imageUri) {
- String mimeType = mContext.getContentResolver().getType(imageUri);
- if (mimeType == null) return ".jpg"; // 默认使用jpg格式
-
- String extension = mimeType.split("/")[1];
- if (extension.equals("jpeg")) return ".jpg";
- if (extension.equals("png")) return ".png";
- if (extension.equals("gif")) return ".gif";
- // 可以添加更多的图片格式
- return "." + extension;
- }
/**
* 将图片路径保存到数据库。
@@ -716,5 +724,30 @@ public class WorkingNote {
}
return null;
}
+ // 获取音频路径
+ public String getAudioPathFromDatabase() {
+ Cursor cursor = mContext.getContentResolver().query(
+ ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId),
+ new String[]{NoteColumns.AUDIO_PATH}, null, null, null);
+
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ String audioPath = cursor.getString(0);
+ cursor.close();
+ return audioPath;
+ }
+ cursor.close();
+ }
+ return null;
+ }
+
+ // 保存音频路径到数据库
+ public void saveAudioPathToDatabase(String audioPath) {
+ ContentValues values = new ContentValues();
+ values.put(NoteColumns.AUDIO_PATH, audioPath);
+ mContext.getContentResolver().update(
+ ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), values, null, null);
+ }
+
}
diff --git a/Src/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/Src/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java
index 3ae60b5..c629a5c 100644
--- a/Src/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java
+++ b/Src/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java
@@ -16,12 +16,14 @@
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.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -29,24 +31,30 @@ 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.media.MediaRecorder;
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.RecognizerIntent;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.text.method.LinkMovementMethod;
import android.text.style.BackgroundColorSpan;
+import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.LayoutInflater;
@@ -82,8 +90,10 @@ import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -200,10 +210,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// initResources(); // 初始化资源
// }
private final int PHOTO_REQUEST=1;
- @Override
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
@@ -228,6 +239,33 @@ public class NoteEditActivity extends Activity implements OnClickListener,
startActivityForResult(loadImage, PHOTO_REQUEST);
}
});
+ // 根据id获取录音按钮
+ final ImageButton record_audio_btn = (ImageButton) findViewById(R.id.record_audio_btn);
+ record_audio_btn.setOnClickListener(new View.OnClickListener() {
+ private boolean isRecording = false;
+
+ @Override
+ public void onClick(View v) {
+ if (!isRecording) {
+ startRecordingAudio();
+ isRecording = true;
+ record_audio_btn.setImageResource(android.R.drawable.ic_media_pause); // 切换为暂停图标
+ } else {
+ stopRecordingAudio();
+ isRecording = false;
+ record_audio_btn.setImageResource(android.R.drawable.ic_btn_speak_now); // 切换为录音图标
+ }
+ }
+ });
+
+ // 根据id获取语音转文字按钮
+ final ImageButton speech_to_text_btn = (ImageButton) findViewById(R.id.speech_to_text_btn);
+ speech_to_text_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startSpeechToText();
+ }
+ });
}
/**
* 当活动被系统销毁后,为了恢复之前的状态,此方法会被调用。
@@ -360,6 +398,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// 获取图片路径并插入图片
String imagePath = mWorkingNote.getImagePathFromDatabase();
+ String audioPath = mWorkingNote.getAudioPathFromDatabase();
if (imagePath != null) {
// 从文件加载图像
Bitmap bitmap = mWorkingNote.loadImageFromFile(imagePath);
@@ -376,11 +415,34 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} else {
Log.e(TAG, "Failed to load image from file: " + imagePath);
}
- } else {
+ }
+ else {
Log.e(TAG, "Image path is null. Can't load image.");
}
}
+ private MediaPlayer mediaPlayer;
+ private void playAudio(String path) {
+ if (mediaPlayer != null) {
+ mediaPlayer.release(); // 释放之前的实例
+ mediaPlayer = null;
+ }
+ mediaPlayer = new MediaPlayer();
+ try {
+ mediaPlayer.setDataSource(path); // 设置音频路径
+ mediaPlayer.prepare(); // 准备播放
+ mediaPlayer.start(); // 开始播放
+
+ // 设置播放完成的回调
+ mediaPlayer.setOnCompletionListener(mp -> {
+ mp.release(); // 播放完成时释放资源
+ mediaPlayer = null;
+ });
+ } catch (IOException e) {
+ e.printStackTrace();
+ Toast.makeText(this, "Unable to play audio", Toast.LENGTH_SHORT).show();
+ }
+ }
/**
* 初始化笔记界面的函数。
@@ -1292,7 +1354,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// public boolean isDownloadsDocument(Uri uri) {
// return "com.android.providers.downloads.documents".equals(uri.getAuthority());
// }
-
+ private static final int REQUEST_RECORD_AUDIO = 101;
+ private static final int REQUEST_SPEECH_INPUT = 102;
+ private String audioFilePath;
//是否为媒体文件
public boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
@@ -1327,6 +1391,31 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "Selected image URI is null."); // 输出错误日志
Toast.makeText(this, "No image selected.", Toast.LENGTH_SHORT).show();
}
+ } // 音频录制处理逻辑
+ else if (requestCode == REQUEST_RECORD_AUDIO && resultCode == RESULT_OK && intent != null) {
+ Uri audioUri = intent.getData();
+ if (audioUri != null) {
+ String audioPath = getRealPathFromURI(audioUri);
+ mWorkingNote.saveAudioPathToDatabase(audioPath);
+ Toast.makeText(this, "Audio recorded successfully!", Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "Audio inserted successfully: " + audioPath);
+ } else {
+ Log.e(TAG, "Recorded audio URI is null.");
+ Toast.makeText(this, "Failed to record audio.", Toast.LENGTH_SHORT).show();
+ }
+ }
+ // 语音转文字处理逻辑
+ else if (requestCode == REQUEST_SPEECH_INPUT && resultCode == RESULT_OK && intent != null) {
+ ArrayList result = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
+ if (result != null && !result.isEmpty()) {
+ String spokenText = result.get(0);
+ mNoteEditor.append(spokenText);
+ Toast.makeText(this, "Speech converted to text!", Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "Speech to text: " + spokenText);
+ } else {
+ Log.e(TAG, "Speech recognition result is null.");
+ Toast.makeText(this, "No speech recognized.", Toast.LENGTH_SHORT).show();
+ }
} else {
Log.e(TAG, "Activity result error, requestCode: " + requestCode + ", resultCode: " + resultCode);
}
@@ -1359,4 +1448,85 @@ public class NoteEditActivity extends Activity implements OnClickListener,
noteEditText.getEditableText().insert(index, spannableString);
}
+
+ private MediaRecorder mediaRecorder;
+
+ private void startRecordingAudio() {
+ try {
+ audioFilePath = getExternalFilesDir(null).getAbsolutePath() + "/audio_" + System.currentTimeMillis() + ".3gp";
+
+ mediaRecorder = new MediaRecorder();
+ mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+ mediaRecorder.setOutputFile(audioFilePath);
+ mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+
+ mediaRecorder.prepare();
+ mediaRecorder.start();
+
+ Toast.makeText(this, "Recording started...", Toast.LENGTH_SHORT).show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Toast.makeText(this, "Recording failed!", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void stopRecordingAudio() {
+ if (mediaRecorder != null) {
+ mediaRecorder.stop();
+ mediaRecorder.release();
+ mediaRecorder = null;
+
+ // 获取音频路径
+ String audioPath = audioFilePath;
+
+ // 插入到光标位置
+ NoteEditText noteEditText = (NoteEditText) findViewById(R.id.note_edit_view);
+ int cursorPosition = noteEditText.getSelectionStart(); // 获取光标位置
+ SpannableString spannableString = new SpannableString("[Audio]");
+ spannableString.setSpan(new ImageSpan(this, R.drawable.ic_audio), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ // 添加播放功能
+ spannableString.setSpan(new ClickableSpan() {
+ @Override
+ public void onClick(View widget) {
+ Log.d("AudioClick", "Audio icon clicked");
+ playAudio(audioPath); // 播放音频
+ }
+ }, 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ Editable editable = noteEditText.getEditableText();
+ editable.insert(cursorPosition, spannableString); // 插入到光标位置
+
+ // 保存音频路径到数据库
+ mWorkingNote.saveAudioPathToDatabase(audioPath);
+ mWorkingNote.saveNote();
+ // 确保编辑器启用点击事件
+ noteEditText.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+ }
+
+
+ private String getRealPathFromURI(Uri contentUri) {
+ Cursor cursor = getContentResolver().query(contentUri, null, null, null, null);
+ if (cursor == null) return contentUri.getPath();
+ cursor.moveToFirst();
+ int idx = cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);
+ String path = cursor.getString(idx);
+ cursor.close();
+ return path;
+ }
+
+ private void startSpeechToText() {
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
+ intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speak now...");
+ try {
+ startActivityForResult(intent, REQUEST_SPEECH_INPUT);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, "Speech to text not supported", Toast.LENGTH_SHORT).show();
+ }
+ }
+
}
diff --git a/Src/app/src/main/java/net/micode/notes/ui/NotesListActivity.java b/Src/app/src/main/java/net/micode/notes/ui/NotesListActivity.java
index b53dfee..fb2f7ce 100644
--- a/Src/app/src/main/java/net/micode/notes/ui/NotesListActivity.java
+++ b/Src/app/src/main/java/net/micode/notes/ui/NotesListActivity.java
@@ -64,6 +64,7 @@ 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.login.LoginActivity;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
@@ -76,6 +77,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.Arrays;
import java.util.HashSet;
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
@@ -173,10 +175,30 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
super.onCreate(savedInstanceState);
setContentView(R.layout.note_list);
initResources();
-
+ // 绑定登出按钮
+ Button logoutButton = (Button) findViewById(R.id.btn_logout);
+ logoutButton.setOnClickListener(v -> logoutUser());
// 用户首次使用时插入介绍信息
setAppInfoFromRawRes();
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // 检查当前用户是否已登录
+ SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
+ int userId = prefs.getInt("user_id", -1);
+
+ if (userId == -1) {
+ // 如果用户未登录,跳转到登录页面
+ Intent intent = new Intent(NotesListActivity.this, LoginActivity.class);
+ startActivity(intent);
+ finish(); // 关闭当前页面
+ } else {
+ // 启动查询以刷新便签列表
+ startAsyncNotesListQuery();
+ }
+ }
/**
* 处理从其他活动返回的结果。
@@ -548,16 +570,37 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* 根据当前文件夹ID选择不同的查询条件,启动一个后台查询处理该查询。
*/
private void startAsyncNotesListQuery() {
- // 根据当前文件夹ID选择查询条件
- String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
- : NORMAL_SELECTION;
- // 启动查询,排序方式为类型降序,修改日期降序
- mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
- Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[]{
- String.valueOf(mCurrentFolderId)
- }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
+ SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
+ int userId = prefs.getInt("user_id", -1);
+ if (userId == -1) {
+ Log.e(TAG, "No user_id found in SharedPreferences, redirecting to login");
+ startActivity(new Intent(this, LoginActivity.class));
+ finish();
+ return;
+ }
+
+ Log.d(TAG, "Loaded user_id: " + userId);
+
+ // 添加 user_id 到查询条件
+ String selection = NoteColumns.USER_ID + "=?";
+ String[] selectionArgs = {String.valueOf(userId)};
+
+ mBackgroundQueryHandler.startQuery(
+ FOLDER_NOTE_LIST_QUERY_TOKEN,
+ null,
+ Notes.CONTENT_NOTE_URI,
+ null,
+ selection,
+ selectionArgs,
+ null
+ );
}
+
+
+
+
+
/**
* 处理后台查询的类。
* 继承自AsyncQueryHandler,用于处理异步查询完成后的操作。
@@ -577,24 +620,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- switch (token) {
- case FOLDER_NOTE_LIST_QUERY_TOKEN:
- // 更新笔记列表适配器的数据源
- mNotesListAdapter.changeCursor(cursor);
- break;
- case FOLDER_LIST_QUERY_TOKEN:
- // 根据查询结果展示或记录错误
- if (cursor != null && cursor.getCount() > 0) {
- showFolderListMenu(cursor);
- } else {
- Log.e(TAG, "Query folder failed");
- }
- break;
- default:
- // 对未知标记不做处理
+ if (token == FOLDER_NOTE_LIST_QUERY_TOKEN) {
+ if (cursor == null || cursor.getCount() == 0) {
+ Log.e("BackgroundQueryHandler", "No notes found.");
return;
+ }
+
+ // 更新适配器的数据
+ mNotesListAdapter.changeCursor(cursor);
+ mNotesListAdapter.notifyDataSetChanged();
+ Log.d("BackgroundQueryHandler", "Adapter updated with new data.");
}
}
+
}
/**
@@ -1301,5 +1339,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
+ private void logoutUser() {
+ // 清空 SharedPreferences 中的用户会话数据
+ SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.clear(); // 清空所有存储的值
+ editor.apply();
+
+ // 跳转到登录页面
+ Intent intent = new Intent(NotesListActivity.this, LoginActivity.class);
+ startActivity(intent);
+
+ // 销毁当前活动,避免返回后仍能访问
+ finish();
+ }
+
+
+
}
diff --git a/Src/app/src/main/res/drawable/ic_audio.png b/Src/app/src/main/res/drawable/ic_audio.png
new file mode 100644
index 0000000..19b0d11
Binary files /dev/null and b/Src/app/src/main/res/drawable/ic_audio.png differ
diff --git a/Src/app/src/main/res/layout/note_edit.xml b/Src/app/src/main/res/layout/note_edit.xml
index 77e2c66..186c27f 100644
--- a/Src/app/src/main/res/layout/note_edit.xml
+++ b/Src/app/src/main/res/layout/note_edit.xml
@@ -115,6 +115,7 @@
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
+
+
+
+
+
+
+
+
+
+
+
+
+