Merge pull request '胶囊功能完善' (#33) from baoerjun_branch into master

master
mbls3xqnp 3 weeks ago
commit 50a82e52bb

@ -4,6 +4,8 @@
xmlns:tools="http://schemas.android.com/tools">
<!-- ==================== 对于功能的声明 ==================== -->
<!-- 允许显示悬浮窗 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- 允许写入外部存储,用于保存笔记数据 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 允许在桌面创建快捷方式 -->

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

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

@ -70,7 +70,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
* onUpgrade
* </p>
*/
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
* <p>
*
* </p>
*
* @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);
}
}
/**
*
*

@ -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);
}
// ==================== 云同步相关方法 ====================

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

@ -1,4 +1,14 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#80000000"/>
<corners android:radius="25dp"/>
</shape>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#A0000000"/>
<corners android:radius="25dp"/>
</shape>
</item>
<item>
<shape>
<solid android:color="#80000000"/>
<corners android:radius="25dp"/>
</shape>
</item>
</selector>

@ -192,7 +192,7 @@
<string name="menu_rich_text">Rich Text</string>
<!-- Capsule Feature -->
<string name="accessibility_service_description">小米便签-全局速记服务,用于监听剪贴板和应用来源</string>
<string name="accessibility_service_description">小米便签全局速记服务。开启后请勿开启“无障碍快捷方式”,以免出现多余的系统悬浮按钮</string>
<string name="capsule_permission_alert_window_title">需要悬浮窗权限</string>
<string name="capsule_permission_alert_window_message">全局速记胶囊需要悬浮窗权限才能显示在其他应用上层。</string>
<string name="capsule_permission_accessibility_title">需要无障碍服务权限</string>

@ -1,15 +1,36 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Sync">
<SwitchPreferenceCompat
android:key="sync_enable"
android:title="Auto Sync"
android:summary="Sync notes automatically"
android:defaultValue="true" />
<PreferenceCategory android:title="通用">
<ListPreference
android:key="pref_theme_mode"
android:title="主题模式"
android:entries="@array/theme_entries"
android:entryValues="@array/theme_values"
android:defaultValue="system" />
</PreferenceCategory>
<PreferenceCategory android:title="安全">
<Preference
android:key="pref_key_security"
android:title="笔记加锁"
android:summary="设置或更改笔记访问密码" />
</PreferenceCategory>
<PreferenceCategory android:title="UI">
<PreferenceCategory android:title="全局速记胶囊">
<SwitchPreferenceCompat
android:key="show_preview"
android:title="Show Preview"
android:defaultValue="true" />
android:key="pref_key_capsule_enable"
android:title="@string/preferences_capsule_title"
android:summary="@string/preferences_capsule_summary"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory android:title="云同步"
android:key="pref_sync_account_key">
<!-- 账户信息将由代码动态插入 -->
</PreferenceCategory>
<PreferenceCategory android:title="关于">
<Preference
android:title="版本"
android:summary="1.0.0" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>

Loading…
Cancel
Save