diff --git a/src/Notesmaster/app/src/main/AndroidManifest.xml b/src/Notesmaster/app/src/main/AndroidManifest.xml
index 96ce6f4..d858d81 100644
--- a/src/Notesmaster/app/src/main/AndroidManifest.xml
+++ b/src/Notesmaster/app/src/main/AndroidManifest.xml
@@ -4,6 +4,8 @@
xmlns:tools="http://schemas.android.com/tools">
+
+
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java b/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java
index 6ed454c..74e1fea 100644
--- a/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java
@@ -6,6 +6,12 @@ import android.util.Log;
import net.micode.notes.auth.UserAuthManager;
import net.micode.notes.data.ThemeRepository;
import net.micode.notes.sync.SyncWorker;
+import net.micode.notes.capsule.CapsuleService;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import androidx.preference.PreferenceManager;
+import android.provider.Settings;
import com.google.android.material.color.DynamicColors;
@@ -28,5 +34,17 @@ public class NotesApplication extends Application {
Log.d(TAG, "EMAS Serverless initialized");
SyncWorker.initialize(this);
+
+ // Start CapsuleService if enabled
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean capsuleEnabled = prefs.getBoolean("pref_key_capsule_enable", false);
+ if (capsuleEnabled && Settings.canDrawOverlays(this)) {
+ Intent intent = new Intent(this, CapsuleService.class);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(intent);
+ } else {
+ startService(intent);
+ }
+ }
}
}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/capsule/CapsuleService.java b/src/Notesmaster/app/src/main/java/net/micode/notes/capsule/CapsuleService.java
index d75bb44..c8c5cc1 100644
--- a/src/Notesmaster/app/src/main/java/net/micode/notes/capsule/CapsuleService.java
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/capsule/CapsuleService.java
@@ -28,14 +28,18 @@ import net.micode.notes.R;
import net.micode.notes.model.Note;
import net.micode.notes.data.Notes;
+import android.view.GestureDetector;
+
public class CapsuleService extends Service {
private static final String TAG = "CapsuleService";
private WindowManager mWindowManager;
private View mCollapsedView;
private View mExpandedView;
+ private EditText mEtContent;
private WindowManager.LayoutParams mCollapsedParams;
private WindowManager.LayoutParams mExpandedParams;
+ private GestureDetector mGestureDetector;
private Handler mHandler = new Handler();
public static String currentSourcePackage = "";
@@ -62,6 +66,11 @@ public class CapsuleService extends Service {
return null;
}
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY;
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -98,14 +107,18 @@ public class CapsuleService extends Service {
mExpandedView = LayoutInflater.from(this).inflate(R.layout.layout_capsule_expanded, null);
mExpandedParams = new WindowManager.LayoutParams(
- dp2px(300),
+ dp2px(320),
dp2px(400),
layoutFlag,
- WindowManager.LayoutParams.FLAG_DIM_BEHIND, // Allow focus for EditText
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
mExpandedParams.dimAmount = 0.5f;
mExpandedParams.gravity = Gravity.CENTER;
+ // Setup Fields
+ mCollapsedView.setClickable(true);
+ mEtContent = mExpandedView.findViewById(R.id.et_content);
+
// Setup Listeners
setupCollapsedListener();
setupExpandedListener();
@@ -120,6 +133,15 @@ public class CapsuleService extends Service {
}
private void setupCollapsedListener() {
+ mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ Log.d(TAG, "onSingleTapConfirmed: Triggering showExpandedView");
+ showExpandedView();
+ return true;
+ }
+ });
+
mCollapsedView.setOnTouchListener(new View.OnTouchListener() {
private int initialX;
private int initialY;
@@ -128,28 +150,33 @@ public class CapsuleService extends Service {
@Override
public boolean onTouch(View v, MotionEvent event) {
+ // Let GestureDetector handle taps
+ if (mGestureDetector.onTouchEvent(event)) {
+ return true;
+ }
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ v.setPressed(true);
initialX = mCollapsedParams.x;
initialY = mCollapsedParams.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
- Log.d(TAG, "onTouch: ACTION_DOWN at " + initialTouchX + ", " + initialTouchY);
return true;
- case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
int Xdiff = (int) (event.getRawX() - initialTouchX);
int Ydiff = (int) (event.getRawY() - initialTouchY);
- Log.d(TAG, "onTouch: ACTION_UP, diff: " + Xdiff + ", " + Ydiff);
- // If click (small movement)
- if (Math.abs(Xdiff) < 10 && Math.abs(Ydiff) < 10) {
- Log.d(TAG, "onTouch: Click detected, showing expanded view");
- showExpandedView();
+
+ // Move if dragged
+ if (Math.abs(Xdiff) > 10 || Math.abs(Ydiff) > 10) {
+ mCollapsedParams.x = initialX + Xdiff;
+ mCollapsedParams.y = initialY + Ydiff;
+ mWindowManager.updateViewLayout(mCollapsedView, mCollapsedParams);
}
return true;
- case MotionEvent.ACTION_MOVE:
- mCollapsedParams.x = initialX + (int) (event.getRawX() - initialTouchX);
- mCollapsedParams.y = initialY + (int) (event.getRawY() - initialTouchY);
- mWindowManager.updateViewLayout(mCollapsedView, mCollapsedParams);
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ v.setPressed(false);
return true;
}
return false;
@@ -195,44 +222,73 @@ public class CapsuleService extends Service {
private void setupExpandedListener() {
Button btnCancel = mExpandedView.findViewById(R.id.btn_cancel);
Button btnSave = mExpandedView.findViewById(R.id.btn_save);
- EditText etContent = mExpandedView.findViewById(R.id.et_content);
btnCancel.setOnClickListener(v -> showCollapsedView());
btnSave.setOnClickListener(v -> {
- String content = etContent.getText().toString();
+ String content = mEtContent.getText().toString();
if (!content.isEmpty()) {
saveNote(content);
- etContent.setText("");
+ mEtContent.setText("");
showCollapsedView();
}
});
}
private void showExpandedView() {
- if (mCollapsedView.getParent() != null) {
- mWindowManager.removeView(mCollapsedView);
- }
- if (mExpandedView.getParent() == null) {
- mWindowManager.addView(mExpandedView, mExpandedParams);
-
- TextView tvSource = mExpandedView.findViewById(R.id.tv_source);
- if (currentSourcePackage != null && !currentSourcePackage.isEmpty()) {
- tvSource.setText("Source: " + currentSourcePackage);
- tvSource.setVisibility(View.VISIBLE);
- } else {
- tvSource.setVisibility(View.GONE);
+ mHandler.post(() -> {
+ try {
+ Log.d(TAG, "showExpandedView: Attempting to show expanded view");
+ if (mCollapsedView != null && mCollapsedView.getParent() != null) {
+ Log.d(TAG, "showExpandedView: Removing collapsed view");
+ mWindowManager.removeViewImmediate(mCollapsedView);
+ }
+
+ if (mExpandedView != null && mExpandedView.getParent() == null) {
+ Log.d(TAG, "showExpandedView: Adding expanded view");
+ mWindowManager.addView(mExpandedView, mExpandedParams);
+
+ TextView tvSource = mExpandedView.findViewById(R.id.tv_source);
+ if (currentSourcePackage != null && !currentSourcePackage.isEmpty()) {
+ tvSource.setText("Source: " + currentSourcePackage);
+ tvSource.setVisibility(View.VISIBLE);
+ } else {
+ tvSource.setVisibility(View.GONE);
+ }
+
+ if (mEtContent != null) {
+ mEtContent.requestFocus();
+ }
+ Log.d(TAG, "showExpandedView: Expanded view added successfully");
+ } else {
+ Log.w(TAG, "showExpandedView: Expanded view already has a parent or is null");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "showExpandedView: Error", e);
}
- }
+ });
}
private void showCollapsedView() {
- if (mExpandedView.getParent() != null) {
- mWindowManager.removeView(mExpandedView);
- }
- if (mCollapsedView.getParent() == null) {
- mWindowManager.addView(mCollapsedView, mCollapsedParams);
- }
+ mHandler.post(() -> {
+ try {
+ Log.d(TAG, "showCollapsedView: Attempting to show collapsed view");
+ if (mExpandedView != null && mExpandedView.getParent() != null) {
+ Log.d(TAG, "showCollapsedView: Removing expanded view");
+ mWindowManager.removeViewImmediate(mExpandedView);
+ }
+
+ if (mCollapsedView != null && mCollapsedView.getParent() == null) {
+ Log.d(TAG, "showCollapsedView: Adding collapsed view");
+ mWindowManager.addView(mCollapsedView, mCollapsedParams);
+ Log.d(TAG, "showCollapsedView: Collapsed view added successfully");
+ } else {
+ Log.w(TAG, "showCollapsedView: Collapsed view already has a parent or is null");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "showCollapsedView: Error", e);
+ }
+ });
}
private void saveNote(String content) {
@@ -243,7 +299,6 @@ public class CapsuleService extends Service {
// 2. Create Note object
Note note = new Note();
- note.setNoteValue(Notes.NoteColumns.ID, String.valueOf(noteId));
note.setTextData(Notes.DataColumns.CONTENT, content);
// Generate Summary (First 20 chars or first line)
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
index b130c5b..00f9092 100644
--- a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
@@ -70,7 +70,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
* 当数据库版本变更时,onUpgrade方法会被调用以执行升级逻辑。
*
*/
- private static final int DB_VERSION = 13;
+ private static final int DB_VERSION = 14;
/**
* 数据库表名常量接口
@@ -437,6 +437,15 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
*/
// 创建模板文件夹
createTemplateFolder(db);
+
+ /**
+ * create capsule folder
+ */
+ // 创建速记胶囊文件夹
+ values.clear();
+ values.put(NoteColumns.ID, Notes.ID_CAPSULE_FOLDER);
+ values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
+ db.insert(TABLE.NOTE, null, values);
}
/**
@@ -600,6 +609,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
oldVersion++;
}
+ // 从V13升级到V14
+ if (oldVersion == 13) {
+ upgradeToV14(db);
+ oldVersion++;
+ }
+
// 如果需要,重新创建触发器
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
@@ -961,6 +976,26 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
}
}
+ /**
+ * 升级数据库到V14版本
+ *
+ * 创建速记胶囊系统文件夹。
+ *
+ *
+ * @param db SQLiteDatabase实例
+ */
+ private void upgradeToV14(SQLiteDatabase db) {
+ try {
+ ContentValues values = new ContentValues();
+ values.put(NoteColumns.ID, Notes.ID_CAPSULE_FOLDER);
+ values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
+ db.insert(TABLE.NOTE, null, values);
+ Log.i(TAG, "Upgraded database to V14: Created Capsule system folder");
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to create Capsule system folder in V14 upgrade", e);
+ }
+ }
+
/**
* 创建模板系统文件夹
*
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/model/Note.java b/src/Notesmaster/app/src/main/java/net/micode/notes/model/Note.java
index 2daf8b7..3b0b856 100644
--- a/src/Notesmaster/app/src/main/java/net/micode/notes/model/Note.java
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/model/Note.java
@@ -425,8 +425,9 @@ public class Note {
try {
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
- return (results == null || results.length == 0 || results[0] == null) ? null
- : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
+ if (results == null || results.length == 0 || results[0] == null) {
+ return null;
+ }
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
@@ -435,7 +436,7 @@ public class Note {
return null;
}
}
- return null;
+ return ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
}
// ==================== 云同步相关方法 ====================
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/CapsuleActionActivity.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/CapsuleActionActivity.java
index dd47212..087e387 100644
--- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/CapsuleActionActivity.java
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/CapsuleActionActivity.java
@@ -41,7 +41,6 @@ public class CapsuleActionActivity extends Activity {
try {
long noteId = Note.getNewNoteId(this, Notes.ID_CAPSULE_FOLDER);
Note note = new Note();
- note.setNoteValue(Notes.NoteColumns.ID, String.valueOf(noteId));
note.setTextData(Notes.DataColumns.CONTENT, content);
String summary = content.length() > 20 ? content.substring(0, 20) + "..." : content;
diff --git a/src/Notesmaster/app/src/main/res/drawable/capsule_collapsed_bg.xml b/src/Notesmaster/app/src/main/res/drawable/capsule_collapsed_bg.xml
index dc7140e..00f1ba2 100644
--- a/src/Notesmaster/app/src/main/res/drawable/capsule_collapsed_bg.xml
+++ b/src/Notesmaster/app/src/main/res/drawable/capsule_collapsed_bg.xml
@@ -1,4 +1,14 @@
-
-
-
-
\ No newline at end of file
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/values/strings.xml b/src/Notesmaster/app/src/main/res/values/strings.xml
index 80148b2..053299e 100644
--- a/src/Notesmaster/app/src/main/res/values/strings.xml
+++ b/src/Notesmaster/app/src/main/res/values/strings.xml
@@ -192,7 +192,7 @@
Rich Text
- 小米便签-全局速记服务,用于监听剪贴板和应用来源。
+ 小米便签全局速记服务。开启后请勿开启“无障碍快捷方式”,以免出现多余的系统悬浮按钮。
需要悬浮窗权限
全局速记胶囊需要悬浮窗权限才能显示在其他应用上层。
需要无障碍服务权限
diff --git a/src/Notesmaster/app/src/main/res/xml/preferences.xml b/src/Notesmaster/app/src/main/res/xml/preferences.xml
index 7746ff9..b1a3b16 100644
--- a/src/Notesmaster/app/src/main/res/xml/preferences.xml
+++ b/src/Notesmaster/app/src/main/res/xml/preferences.xml
@@ -1,15 +1,36 @@
-
-
+
+
+
+
+
+
-
+
+
+ android:key="pref_key_capsule_enable"
+ android:title="@string/preferences_capsule_title"
+ android:summary="@string/preferences_capsule_summary"
+ android:defaultValue="false" />
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+