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 +