Merge pull request 'audio' (#6) from b into main

main
pbv5msopc 7 months ago
commit ef8f5e9c59

@ -6,6 +6,8 @@
android:versionName="0.1">
<!-- 应用需要的权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

@ -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接口定义数据列

@ -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表的触发器

@ -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; // 用户不存在
}
}

@ -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,17 +48,29 @@ 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() {

@ -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);

@ -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;
}

@ -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);
}
}

@ -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<String> 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();
}
}
}

@ -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();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -115,6 +115,7 @@
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
</LinearLayout>
</LinearLayout>
<ImageButton
android:id="@+id/add_img_btn"
@ -124,6 +125,29 @@
android:layout_marginTop="600dp"
android:layout_marginBottom="7dp"
android:src="@android:drawable/ic_menu_gallery" />
<!-- 录音按钮 -->
<ImageButton
android:id="@+id/record_audio_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="350dp"
android:layout_marginTop="600dp"
android:layout_marginBottom="7dp"
android:src="@android:drawable/ic_btn_speak_now"
android:contentDescription="Record Audio" />
<!-- 语音转文字按钮 -->
<ImageButton
android:id="@+id/speech_to_text_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="300dp"
android:layout_marginTop="600dp"
android:layout_marginBottom="7dp"
android:src="@android:drawable/ic_menu_sort_by_size"
android:contentDescription="Speech to Text"/>
<!-- 背景颜色设置按钮 -->
<ImageView
android:id="@+id/btn_set_bg_color"

@ -49,4 +49,16 @@
android:layout_height="wrap_content"
android:focusable="false"
android:layout_gravity="bottom" />
<!--添加一个登出按钮-->
<Button
android:id="@+id/btn_logout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:text="退出登录"
android:textColor="@android:color/white" />
</FrameLayout>

Loading…
Cancel
Save