parent
2e7dc5d079
commit
87b5daed82
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,339 @@
|
||||
package net.micode.notes.capsule;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.DragEvent;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.model.Note;
|
||||
import net.micode.notes.data.Notes;
|
||||
|
||||
public class CapsuleService extends Service {
|
||||
|
||||
private static final String TAG = "CapsuleService";
|
||||
private WindowManager mWindowManager;
|
||||
private View mCollapsedView;
|
||||
private View mExpandedView;
|
||||
private WindowManager.LayoutParams mCollapsedParams;
|
||||
private WindowManager.LayoutParams mExpandedParams;
|
||||
|
||||
private Handler mHandler = new Handler();
|
||||
public static String currentSourcePackage = "";
|
||||
|
||||
private static final String CHANNEL_ID = "CapsuleServiceChannel";
|
||||
|
||||
public static final String ACTION_SAVE_SUCCESS = "net.micode.notes.capsule.ACTION_SAVE_SUCCESS";
|
||||
|
||||
private final android.content.BroadcastReceiver mSaveReceiver = new android.content.BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_SAVE_SUCCESS.equals(intent.getAction())) {
|
||||
highlightCapsule();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static void setCurrentSourcePackage(String pkg) {
|
||||
currentSourcePackage = pkg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
createNotificationChannel();
|
||||
startForeground(1, createNotification());
|
||||
|
||||
initViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
// Collapsed View
|
||||
mCollapsedView = LayoutInflater.from(this).inflate(R.layout.layout_capsule_collapsed, null);
|
||||
|
||||
int layoutFlag;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
layoutFlag = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
} else {
|
||||
layoutFlag = WindowManager.LayoutParams.TYPE_PHONE;
|
||||
}
|
||||
|
||||
mCollapsedParams = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
layoutFlag,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
|
||||
mCollapsedParams.gravity = Gravity.TOP | Gravity.START;
|
||||
mCollapsedParams.x = 0;
|
||||
mCollapsedParams.y = 100;
|
||||
|
||||
// Expanded View
|
||||
mExpandedView = LayoutInflater.from(this).inflate(R.layout.layout_capsule_expanded, null);
|
||||
|
||||
mExpandedParams = new WindowManager.LayoutParams(
|
||||
dp2px(300),
|
||||
dp2px(400),
|
||||
layoutFlag,
|
||||
WindowManager.LayoutParams.FLAG_DIM_BEHIND, // Allow focus for EditText
|
||||
PixelFormat.TRANSLUCENT);
|
||||
mExpandedParams.dimAmount = 0.5f;
|
||||
mExpandedParams.gravity = Gravity.CENTER;
|
||||
|
||||
// Setup Listeners
|
||||
setupCollapsedListener();
|
||||
setupExpandedListener();
|
||||
|
||||
// Add Collapsed View initially
|
||||
try {
|
||||
mWindowManager.addView(mCollapsedView, mCollapsedParams);
|
||||
Log.d(TAG, "initViews: Collapsed view added");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initViews: Failed to add collapsed view", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupCollapsedListener() {
|
||||
mCollapsedView.setOnTouchListener(new View.OnTouchListener() {
|
||||
private int initialX;
|
||||
private int initialY;
|
||||
private float initialTouchX;
|
||||
private float initialTouchY;
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
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:
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mCollapsedParams.x = initialX + (int) (event.getRawX() - initialTouchX);
|
||||
mCollapsedParams.y = initialY + (int) (event.getRawY() - initialTouchY);
|
||||
mWindowManager.updateViewLayout(mCollapsedView, mCollapsedParams);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mCollapsedView.setOnDragListener((v, event) -> {
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
Log.d(TAG, "onDrag: ACTION_DRAG_STARTED");
|
||||
if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN) ||
|
||||
event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
|
||||
v.setAlpha(1.0f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
Log.d(TAG, "onDrag: ACTION_DRAG_ENTERED");
|
||||
v.animate().scaleX(1.2f).scaleY(1.2f).setDuration(200).start();
|
||||
return true;
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
Log.d(TAG, "onDrag: ACTION_DRAG_EXITED");
|
||||
v.animate().scaleX(1.0f).scaleY(1.0f).setDuration(200).start();
|
||||
return true;
|
||||
case DragEvent.ACTION_DROP:
|
||||
Log.d(TAG, "onDrag: ACTION_DROP");
|
||||
ClipData.Item item = event.getClipData().getItemAt(0);
|
||||
CharSequence text = item.getText();
|
||||
if (text != null) {
|
||||
saveNote(text.toString());
|
||||
}
|
||||
v.animate().scaleX(1.0f).scaleY(1.0f).setDuration(200).start();
|
||||
return true;
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
Log.d(TAG, "onDrag: ACTION_DRAG_ENDED");
|
||||
v.setAlpha(0.8f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
if (!content.isEmpty()) {
|
||||
saveNote(content);
|
||||
etContent.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showCollapsedView() {
|
||||
if (mExpandedView.getParent() != null) {
|
||||
mWindowManager.removeView(mExpandedView);
|
||||
}
|
||||
if (mCollapsedView.getParent() == null) {
|
||||
mWindowManager.addView(mCollapsedView, mCollapsedParams);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveNote(String content) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// 1. Create new note in CAPSULE folder
|
||||
long noteId = Note.getNewNoteId(this, Notes.ID_CAPSULE_FOLDER);
|
||||
|
||||
// 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)
|
||||
String summary = content.length() > 20 ? content.substring(0, 20) + "..." : content;
|
||||
int firstLineEnd = content.indexOf('\n');
|
||||
if (firstLineEnd > 0 && firstLineEnd < 20) {
|
||||
summary = content.substring(0, firstLineEnd);
|
||||
}
|
||||
note.setNoteValue(Notes.NoteColumns.SNIPPET, summary);
|
||||
|
||||
// Add Source Info if available
|
||||
if (currentSourcePackage != null && !currentSourcePackage.isEmpty()) {
|
||||
note.setTextData(Notes.DataColumns.DATA3, currentSourcePackage);
|
||||
}
|
||||
|
||||
boolean success = note.syncNote(this, noteId);
|
||||
|
||||
mHandler.post(() -> {
|
||||
if (success) {
|
||||
Log.d(TAG, "saveNote: Success");
|
||||
Toast.makeText(this, "Saved to Notes", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Log.e(TAG, "saveNote: Failed");
|
||||
Toast.makeText(this, "Failed to save", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "saveNote: Exception", e);
|
||||
mHandler.post(() -> Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private int dp2px(int dp) {
|
||||
return (int) (dp * getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel serviceChannel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Capsule Service Channel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
if (manager != null) {
|
||||
manager.createNotificationChannel(serviceChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
Notification.Builder builder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder = new Notification.Builder(this, CHANNEL_ID);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
|
||||
return builder.setContentTitle("Global Capsule Running")
|
||||
.setContentText("Tap to configure")
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
try {
|
||||
unregisterReceiver(mSaveReceiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Receiver not registered", e);
|
||||
}
|
||||
if (mCollapsedView != null && mCollapsedView.getParent() != null) {
|
||||
mWindowManager.removeView(mCollapsedView);
|
||||
}
|
||||
if (mExpandedView != null && mExpandedView.getParent() != null) {
|
||||
mWindowManager.removeView(mExpandedView);
|
||||
}
|
||||
}
|
||||
|
||||
private void highlightCapsule() {
|
||||
if (mCollapsedView != null && mCollapsedView.getParent() != null) {
|
||||
mHandler.post(() -> {
|
||||
mCollapsedView.animate().scaleX(1.5f).scaleY(1.5f).setDuration(200).withEndAction(() -> {
|
||||
mCollapsedView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(200).start();
|
||||
}).start();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package net.micode.notes.capsule;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ClipboardMonitorService extends AccessibilityService {
|
||||
|
||||
private ClipboardManager mClipboardManager;
|
||||
private ClipboardManager.OnPrimaryClipChangedListener mClipListener;
|
||||
private long mLastClipTime = 0;
|
||||
private static final long MERGE_THRESHOLD = 2000; // 2 seconds
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mClipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceConnected() {
|
||||
super.onServiceConnected();
|
||||
// Register clipboard listener
|
||||
if (mClipboardManager != null) {
|
||||
mClipListener = () -> {
|
||||
handleClipChanged();
|
||||
};
|
||||
mClipboardManager.addPrimaryClipChangedListener(mClipListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleClipChanged() {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - mLastClipTime < MERGE_THRESHOLD) {
|
||||
// Notify CapsuleService to show "Merge" bubble
|
||||
// For now just show a toast or log
|
||||
// Intent intent = new Intent("net.micode.notes.capsule.ACTION_MERGE_SUGGESTION");
|
||||
// sendBroadcast(intent);
|
||||
}
|
||||
mLastClipTime = now;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
if (event.getPackageName() != null) {
|
||||
// Store current package name in CapsuleService
|
||||
CapsuleService.setCurrentSourcePackage(event.getPackageName().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mClipboardManager != null && mClipListener != null) {
|
||||
mClipboardManager.removePrimaryClipChangedListener(mClipListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
import android.util.Log;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.model.Note;
|
||||
import net.micode.notes.capsule.CapsuleService;
|
||||
|
||||
public class CapsuleActionActivity extends Activity {
|
||||
|
||||
private static final String TAG = "CapsuleActionActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
CharSequence text = getIntent().getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
|
||||
String sourcePackage = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
if (getReferrer() != null) {
|
||||
sourcePackage = getReferrer().getAuthority(); // or getHost()
|
||||
}
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
saveNote(text.toString(), sourcePackage);
|
||||
|
||||
// Notify CapsuleService to animate (if running)
|
||||
Intent intent = new Intent("net.micode.notes.capsule.ACTION_SAVE_SUCCESS");
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private void saveNote(String content, String source) {
|
||||
new Thread(() -> {
|
||||
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;
|
||||
int firstLineEnd = content.indexOf('\n');
|
||||
if (firstLineEnd > 0 && firstLineEnd < 20) {
|
||||
summary = content.substring(0, firstLineEnd);
|
||||
}
|
||||
note.setNoteValue(Notes.NoteColumns.SNIPPET, summary);
|
||||
|
||||
if (source != null) {
|
||||
note.setTextData(Notes.DataColumns.DATA3, source);
|
||||
}
|
||||
|
||||
boolean success = note.syncNote(this, noteId);
|
||||
|
||||
runOnUiThread(() -> {
|
||||
if (success) {
|
||||
Toast.makeText(this, "已保存到胶囊", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,164 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.model.Note;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class CapsuleListFragment extends Fragment {
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private CapsuleAdapter mAdapter;
|
||||
private TextView mEmptyView;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_capsule_list, container, false);
|
||||
mRecyclerView = view.findViewById(R.id.capsule_list);
|
||||
mEmptyView = view.findViewById(R.id.tv_empty);
|
||||
|
||||
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mAdapter = new CapsuleAdapter();
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadCapsules();
|
||||
}
|
||||
|
||||
private void loadCapsules() {
|
||||
new Thread(() -> {
|
||||
if (getContext() == null) return;
|
||||
|
||||
// Query notes in CAPSULE folder
|
||||
String selection = Notes.NoteColumns.PARENT_ID + "=?";
|
||||
String[] selectionArgs = new String[]{String.valueOf(Notes.ID_CAPSULE_FOLDER)};
|
||||
|
||||
Cursor cursor = getContext().getContentResolver().query(
|
||||
Notes.CONTENT_NOTE_URI,
|
||||
null,
|
||||
selection,
|
||||
selectionArgs,
|
||||
Notes.NoteColumns.MODIFIED_DATE + " DESC"
|
||||
);
|
||||
|
||||
List<CapsuleItem> items = new ArrayList<>();
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.ID));
|
||||
String snippet = cursor.getString(cursor.getColumnIndexOrThrow(Notes.NoteColumns.SNIPPET));
|
||||
long modifiedDate = cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.MODIFIED_DATE));
|
||||
|
||||
// We need to fetch DATA3 (source) which is in DATA table.
|
||||
// For performance, we might do a join or lazy load.
|
||||
// For now, let's just use snippet and date.
|
||||
// To get Source, we really should query DATA table or use a projection if CONTENT_NOTE_URI supports joining.
|
||||
// NotesProvider usually joins. Let's check NoteColumns.
|
||||
// Notes.DataColumns.DATA3 is NOT in NoteColumns.
|
||||
|
||||
items.add(new CapsuleItem(id, snippet, modifiedDate, "Loading source..."));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
// Update UI
|
||||
if (getActivity() != null) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
mAdapter.setItems(items);
|
||||
mEmptyView.setVisibility(items.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private static class CapsuleItem {
|
||||
long id;
|
||||
String summary;
|
||||
long time;
|
||||
String source;
|
||||
|
||||
public CapsuleItem(long id, String summary, long time, String source) {
|
||||
this.id = id;
|
||||
this.summary = summary;
|
||||
this.time = time;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
private class CapsuleAdapter extends RecyclerView.Adapter<CapsuleAdapter.ViewHolder> {
|
||||
private List<CapsuleItem> mItems = new ArrayList<>();
|
||||
|
||||
public void setItems(List<CapsuleItem> items) {
|
||||
mItems = items;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_capsule, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
CapsuleItem item = mItems.get(position);
|
||||
holder.tvSummary.setText(item.summary);
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());
|
||||
holder.tvTime.setText(sdf.format(new Date(item.time)));
|
||||
|
||||
if (item.source != null && !item.source.isEmpty()) {
|
||||
holder.tvSource.setText("Source: " + item.source);
|
||||
holder.tvSource.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.tvSource.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
// Open Note Edit
|
||||
// We need to implement this
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvSummary, tvTime, tvSource;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvSummary = itemView.findViewById(R.id.tv_summary);
|
||||
tvTime = itemView.findViewById(R.id.tv_time);
|
||||
tvSource = itemView.findViewById(R.id.tv_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.NotesRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FolderAdapter extends RecyclerView.Adapter<FolderAdapter.FolderViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private List<NotesRepository.NoteInfo> folders;
|
||||
private long selectedFolderId = -1;
|
||||
private OnFolderClickListener listener;
|
||||
|
||||
public interface OnFolderClickListener {
|
||||
void onFolderClick(long folderId);
|
||||
}
|
||||
|
||||
public FolderAdapter(Context context) {
|
||||
this.context = context;
|
||||
this.folders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void setFolders(List<NotesRepository.NoteInfo> folders) {
|
||||
this.folders = folders != null ? folders : new ArrayList<>();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setSelectedFolderId(long folderId) {
|
||||
this.selectedFolderId = folderId;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setOnFolderClickListener(OnFolderClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FolderViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.folder_tab_item, parent, false);
|
||||
return new FolderViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull FolderViewHolder holder, int position) {
|
||||
NotesRepository.NoteInfo folder = folders.get(position);
|
||||
holder.bind(folder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return folders.size();
|
||||
}
|
||||
|
||||
class FolderViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvName;
|
||||
|
||||
public FolderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
tvName = itemView.findViewById(R.id.tv_folder_name);
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
int pos = getAdapterPosition();
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
listener.onFolderClick(folders.get(pos).getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bind(NotesRepository.NoteInfo folder) {
|
||||
String name = folder.snippet; // Folder name is stored in snippet
|
||||
if (name == null || name.isEmpty()) {
|
||||
name = "Folder";
|
||||
}
|
||||
tvName.setText(name);
|
||||
tvName.setSelected(folder.getId() == selectedFolderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import android.app.AlertDialog;
|
||||
import android.text.InputType;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.NotesRepository;
|
||||
import net.micode.notes.databinding.NoteListBinding;
|
||||
import net.micode.notes.viewmodel.NotesListViewModel;
|
||||
|
||||
public class NotesListFragment extends Fragment implements
|
||||
NoteInfoAdapter.OnNoteItemClickListener,
|
||||
NoteInfoAdapter.OnNoteItemLongClickListener {
|
||||
|
||||
private static final String TAG = "NotesListFragment";
|
||||
private static final String PREF_KEY_IS_STAGGERED = "is_staggered";
|
||||
|
||||
private NotesListViewModel viewModel;
|
||||
private NoteListBinding binding;
|
||||
private NoteInfoAdapter adapter;
|
||||
|
||||
private static final int REQUEST_CODE_OPEN_NODE = 102;
|
||||
private static final int REQUEST_CODE_NEW_NODE = 103;
|
||||
private static final int REQUEST_CODE_VERIFY_PASSWORD_FOR_OPEN = 107;
|
||||
|
||||
private NotesRepository.NoteInfo pendingNote;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
binding = NoteListBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
initViewModel();
|
||||
initViews(view);
|
||||
observeViewModel();
|
||||
}
|
||||
|
||||
private void initViewModel() {
|
||||
NotesRepository repository = new NotesRepository(requireContext().getContentResolver());
|
||||
// Use requireActivity() to share ViewModel with Activity (for Sidebar filtering)
|
||||
viewModel = new ViewModelProvider(requireActivity(),
|
||||
new ViewModelProvider.Factory() {
|
||||
@Override
|
||||
public <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass) {
|
||||
return (T) new NotesListViewModel(repository);
|
||||
}
|
||||
}).get(NotesListViewModel.class);
|
||||
}
|
||||
|
||||
private void initViews(View view) {
|
||||
adapter = new NoteInfoAdapter(requireContext());
|
||||
binding.notesList.setAdapter(adapter);
|
||||
|
||||
// Restore layout preference
|
||||
boolean isStaggered = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getBoolean(PREF_KEY_IS_STAGGERED, true);
|
||||
setLayoutManager(isStaggered);
|
||||
|
||||
adapter.setOnNoteItemClickListener(this);
|
||||
adapter.setOnNoteItemLongClickListener(this);
|
||||
|
||||
// Fix FAB: Enable creating new notes
|
||||
binding.btnNewNote.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(getActivity(), NoteEditActivity.class);
|
||||
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
|
||||
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, viewModel.getCurrentFolderId());
|
||||
startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
|
||||
});
|
||||
}
|
||||
|
||||
private void setLayoutManager(boolean isStaggered) {
|
||||
if (isStaggered) {
|
||||
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
|
||||
layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
|
||||
binding.notesList.setLayoutManager(layoutManager);
|
||||
} else {
|
||||
binding.notesList.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean toggleLayout() {
|
||||
boolean isStaggered = binding.notesList.getLayoutManager() instanceof StaggeredGridLayoutManager;
|
||||
boolean newIsStaggered = !isStaggered;
|
||||
|
||||
setLayoutManager(newIsStaggered);
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.edit()
|
||||
.putBoolean(PREF_KEY_IS_STAGGERED, newIsStaggered)
|
||||
.apply();
|
||||
|
||||
return newIsStaggered;
|
||||
}
|
||||
|
||||
public boolean isStaggeredLayout() {
|
||||
return binding.notesList.getLayoutManager() instanceof StaggeredGridLayoutManager;
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getNotesLiveData().observe(getViewLifecycleOwner(), notes -> {
|
||||
adapter.setNotes(notes);
|
||||
});
|
||||
|
||||
viewModel.getIsSelectionMode().observe(getViewLifecycleOwner(), isSelection -> {
|
||||
adapter.setSelectionMode(isSelection);
|
||||
});
|
||||
|
||||
viewModel.getSelectedIdsLiveData().observe(getViewLifecycleOwner(), selectedIds -> {
|
||||
adapter.setSelectedIds(selectedIds);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoteItemClick(int position, long noteId) {
|
||||
if (Boolean.TRUE.equals(viewModel.getIsSelectionMode().getValue())) {
|
||||
boolean isSelected = viewModel.getSelectedIdsLiveData().getValue() != null &&
|
||||
viewModel.getSelectedIdsLiveData().getValue().contains(noteId);
|
||||
viewModel.toggleNoteSelection(noteId, !isSelected);
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewModel.getNotesLiveData().getValue() != null && position < viewModel.getNotesLiveData().getValue().size()) {
|
||||
NotesRepository.NoteInfo note = viewModel.getNotesLiveData().getValue().get(position);
|
||||
if (note.type == Notes.TYPE_FOLDER) {
|
||||
viewModel.enterFolder(note.getId());
|
||||
} else {
|
||||
if (note.isLocked) {
|
||||
pendingNote = note;
|
||||
Intent intent = new Intent(getActivity(), PasswordActivity.class);
|
||||
intent.setAction(PasswordActivity.ACTION_CHECK_PASSWORD);
|
||||
startActivityForResult(intent, REQUEST_CODE_VERIFY_PASSWORD_FOR_OPEN);
|
||||
} else {
|
||||
openNoteEditor(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_CODE_VERIFY_PASSWORD_FOR_OPEN && resultCode == android.app.Activity.RESULT_OK) {
|
||||
if (pendingNote != null) {
|
||||
openNoteEditor(pendingNote);
|
||||
pendingNote = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoteItemLongClick(int position, long noteId) {
|
||||
if (!Boolean.TRUE.equals(viewModel.getIsSelectionMode().getValue())) {
|
||||
viewModel.setIsSelectionMode(true);
|
||||
viewModel.toggleNoteSelection(noteId, true);
|
||||
} else {
|
||||
boolean isSelected = viewModel.getSelectedIdsLiveData().getValue() != null &&
|
||||
viewModel.getSelectedIdsLiveData().getValue().contains(noteId);
|
||||
viewModel.toggleNoteSelection(noteId, !isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated Context Menu
|
||||
private void showContextMenu(NotesRepository.NoteInfo note) {
|
||||
// ... kept for reference or removed
|
||||
}
|
||||
|
||||
private void openNoteEditor(NotesRepository.NoteInfo note) {
|
||||
Intent intent = new Intent(getActivity(), NoteEditActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, note.getParentId());
|
||||
intent.putExtra(Intent.EXTRA_UID, note.getId());
|
||||
startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
viewModel.refreshNotes();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.tool.ResourceParser;
|
||||
|
||||
public class NotesRecyclerAdapter extends RecyclerView.Adapter<NotesRecyclerAdapter.NoteViewHolder> {
|
||||
|
||||
private Context mContext;
|
||||
private Cursor mCursor;
|
||||
private OnNoteItemClickListener mListener;
|
||||
private boolean mChoiceMode;
|
||||
|
||||
public interface OnNoteItemClickListener {
|
||||
void onNoteClick(int position, long noteId);
|
||||
boolean onNoteLongClick(int position, long noteId);
|
||||
}
|
||||
|
||||
public NotesRecyclerAdapter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setOnNoteItemClickListener(OnNoteItemClickListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void swapCursor(Cursor newCursor) {
|
||||
if (mCursor == newCursor) return;
|
||||
if (mCursor != null) {
|
||||
mCursor.close();
|
||||
}
|
||||
mCursor = newCursor;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public NoteViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.note_item, parent, false);
|
||||
return new NoteViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull NoteViewHolder holder, int position) {
|
||||
if (mCursor == null || !mCursor.moveToPosition(position)) {
|
||||
return;
|
||||
}
|
||||
NoteItemData itemData = new NoteItemData(mContext, mCursor);
|
||||
holder.bind(itemData, mChoiceMode, false); // Checked logic omitted for now
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (mListener != null) {
|
||||
mListener.onNoteClick(position, itemData.getId());
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (mListener != null) {
|
||||
return mListener.onNoteLongClick(position, itemData.getId());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mCursor == null ? 0 : mCursor.getCount();
|
||||
}
|
||||
|
||||
class NoteViewHolder extends RecyclerView.ViewHolder {
|
||||
CardView cardView;
|
||||
TextView title, time, name;
|
||||
ImageView typeIcon, lockIcon, alertIcon;
|
||||
CheckBox checkBox;
|
||||
|
||||
public NoteViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
// Since root is CardView
|
||||
cardView = (CardView) itemView;
|
||||
title = itemView.findViewById(R.id.tv_title);
|
||||
time = itemView.findViewById(R.id.tv_time);
|
||||
name = itemView.findViewById(R.id.tv_name);
|
||||
checkBox = itemView.findViewById(android.R.id.checkbox);
|
||||
typeIcon = itemView.findViewById(R.id.iv_type_icon);
|
||||
lockIcon = itemView.findViewById(R.id.iv_lock_icon);
|
||||
alertIcon = itemView.findViewById(R.id.iv_alert_icon);
|
||||
}
|
||||
|
||||
public void bind(NoteItemData data, boolean choiceMode, boolean checked) {
|
||||
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
|
||||
checkBox.setVisibility(View.VISIBLE);
|
||||
checkBox.setChecked(checked);
|
||||
} else {
|
||||
checkBox.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (data.getType() == Notes.TYPE_FOLDER) {
|
||||
String snippet = data.getSnippet();
|
||||
if (snippet == null) snippet = "";
|
||||
title.setText(snippet + " (" + data.getNotesCount() + ")");
|
||||
time.setVisibility(View.GONE);
|
||||
typeIcon.setVisibility(View.VISIBLE);
|
||||
typeIcon.setImageResource(R.drawable.ic_folder);
|
||||
cardView.setCardBackgroundColor(mContext.getColor(R.color.bg_white));
|
||||
} else {
|
||||
typeIcon.setVisibility(View.GONE);
|
||||
time.setVisibility(View.VISIBLE);
|
||||
String titleStr = data.getTitle();
|
||||
if (titleStr == null || titleStr.isEmpty()) {
|
||||
titleStr = data.getSnippet();
|
||||
}
|
||||
title.setText(titleStr);
|
||||
time.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
|
||||
|
||||
// Background Color
|
||||
int colorId = data.getBgColorId();
|
||||
int color = ResourceParser.getNoteBgColor(mContext, colorId);
|
||||
cardView.setCardBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import net.micode.notes.R;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_settings);
|
||||
if (savedInstanceState == null) {
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings_container, new SettingsFragment())
|
||||
.commit();
|
||||
}
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.menu_settings);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.model.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TaskListFragment extends Fragment implements TaskListAdapter.OnTaskItemClickListener {
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private TaskListAdapter adapter;
|
||||
private FloatingActionButton fab;
|
||||
private static final int REQUEST_EDIT_TASK = 1001;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.activity_task_list, container, false);
|
||||
|
||||
// Hide Toolbar in fragment if Activity has one or Tabs
|
||||
// For now, let's keep it but remove navigation logic or hide it if needed
|
||||
View toolbar = view.findViewById(R.id.toolbar);
|
||||
if (toolbar != null) {
|
||||
// toolbar.setVisibility(View.GONE); // Optional: Hide if using main tabs
|
||||
}
|
||||
|
||||
recyclerView = view.findViewById(R.id.task_list_view);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
adapter = new TaskListAdapter(getContext(), this);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
fab = view.findViewById(R.id.btn_new_task);
|
||||
fab.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(getActivity(), TaskEditActivity.class);
|
||||
startActivityForResult(intent, REQUEST_EDIT_TASK);
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadTasks();
|
||||
}
|
||||
|
||||
private void loadTasks() {
|
||||
new Thread(() -> {
|
||||
if (getContext() == null) return;
|
||||
Cursor cursor = getContext().getContentResolver().query(
|
||||
Notes.CONTENT_NOTE_URI,
|
||||
null,
|
||||
NoteColumns.TYPE + "=?",
|
||||
new String[]{String.valueOf(Notes.TYPE_TASK)},
|
||||
null
|
||||
);
|
||||
|
||||
List<Task> tasks = new ArrayList<>();
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
tasks.add(Task.fromCursor(cursor));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
if (getActivity() != null) {
|
||||
getActivity().runOnUiThread(() -> adapter.setTasks(tasks));
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(Task task) {
|
||||
Intent intent = new Intent(getActivity(), TaskEditActivity.class);
|
||||
intent.putExtra(Intent.EXTRA_UID, task.id);
|
||||
startActivityForResult(intent, REQUEST_EDIT_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckBoxClick(Task task) {
|
||||
task.status = (task.status == Task.STATUS_ACTIVE) ? Task.STATUS_COMPLETED : Task.STATUS_ACTIVE;
|
||||
if (task.status == Task.STATUS_COMPLETED) {
|
||||
task.finishedTime = System.currentTimeMillis();
|
||||
} else {
|
||||
task.finishedTime = 0;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
if (getContext() != null) {
|
||||
task.save(getContext());
|
||||
if (getActivity() != null) {
|
||||
getActivity().runOnUiThread(() -> loadTasks());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_EDIT_TASK && resultCode == android.app.Activity.RESULT_OK) {
|
||||
loadTasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true" android:color="@color/text_color_primary"/>
|
||||
<item android:color="@color/text_color_secondary"/>
|
||||
</selector>
|
||||
@ -0,0 +1,4 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#80000000"/>
|
||||
<corners android:radius="25dp"/>
|
||||
</shape>
|
||||
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true">
|
||||
<shape>
|
||||
<solid android:color="#FFD54F"/> <!-- Yellow -->
|
||||
<corners android:radius="16dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<corners android:radius="16dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFC107"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#757575"
|
||||
android:strokeWidth="2"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M4,11h5V5H4v6zm0,7h5v-6H4v6zm6,0h5v-6h-5v6zm6,0h5v-6h-5v6zm-6,-7h5V5h-5v6zm6,-6v6h5V5h-5z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3,13h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2V7H3v2zm4,4h14v-2H7v2zm0,4h14v-2H7v2zM7,7v2h14V7H7z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#F0F0F0" />
|
||||
<corners android:radius="24dp" />
|
||||
</shape>
|
||||
@ -0,0 +1,4 @@
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true" android:drawable="@drawable/ic_checkbox_checked_round" />
|
||||
<item android:drawable="@drawable/ic_checkbox_unchecked_round" />
|
||||
</selector>
|
||||
@ -0,0 +1,215 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_color">
|
||||
|
||||
<!-- Normal Header Container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/header_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_color"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<!-- Top Bar (Hamburger + Title) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/top_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_sidebar"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/ic_menu_hamburger"
|
||||
app:tint="@color/text_color_primary"
|
||||
android:padding="4dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Menu"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/app_name"
|
||||
android:textAppearance="@style/NotesBigTitle"
|
||||
android:layout_marginStart="12dp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_change_layout"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/ic_view_list"
|
||||
app:tint="@color/text_color_primary"
|
||||
android:padding="4dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Change Layout"
|
||||
android:visibility="visible"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<TextView
|
||||
android:id="@+id/tv_search_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/search_bar_bg"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="Search notes"
|
||||
android:textColor="#9E9E9E"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- Category Tabs -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_folder_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/folder_tab_item" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ViewPager -->
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_container"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_navigation"/>
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottom_navigation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/bg_white"
|
||||
app:elevation="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:menu="@menu/bottom_nav_menu"
|
||||
app:labelVisibilityMode="labeled"
|
||||
app:itemIconTint="@color/selector_bottom_nav_color"
|
||||
app:itemTextColor="@color/selector_bottom_nav_color"/>
|
||||
|
||||
<!-- Selection Header Overlay -->
|
||||
<LinearLayout
|
||||
android:id="@+id/selection_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/bg_white"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:visibility="gone"
|
||||
android:elevation="4dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_close_selection"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
||||
app:tint="@color/text_color_primary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_selection_count"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="已选择 0 项"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text_color_primary"
|
||||
android:layout_marginStart="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btn_select_all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="全选"
|
||||
android:textColor="@color/text_color_primary"
|
||||
android:textSize="16sp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btn_selection_restore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="恢复"
|
||||
android:textColor="@color/text_color_primary"
|
||||
android:textSize="16sp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="8dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btn_selection_delete_forever"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="永久删除"
|
||||
android:textColor="@color/text_color_primary"
|
||||
android:textSize="16sp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="8dp"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Selection Bottom Bar Overlay -->
|
||||
<LinearLayout
|
||||
android:id="@+id/selection_bottom_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/bg_white"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:elevation="10dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<TextView android:id="@+id/btn_action_hide" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="隐藏" android:drawableTop="@android:drawable/ic_menu_close_clear_cancel" android:gravity="center" android:visibility="gone"/>
|
||||
<TextView android:id="@+id/btn_action_pin" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="置顶" android:drawableTop="@android:drawable/ic_menu_upload" android:gravity="center"/>
|
||||
<TextView android:id="@+id/btn_action_move" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="移动到" android:drawableTop="@drawable/ic_folder" android:gravity="center"/>
|
||||
<TextView android:id="@+id/btn_action_lock" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="加锁" android:drawableTop="@android:drawable/ic_lock_lock" android:gravity="center"/>
|
||||
<TextView android:id="@+id/btn_action_delete" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="删除" android:drawableTop="@android:drawable/ic_menu_delete" android:gravity="center"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/sidebar_fragment"
|
||||
android:name="net.micode.notes.ui.SidebarFragment"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
tools:layout="@layout/sidebar_layout" />
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tv_folder_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Folder"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_color_primary"
|
||||
android:background="@drawable/folder_tab_bg_selector"
|
||||
android:fontFamily="sans-serif-medium" />
|
||||
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_color">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/Theme.Notesmaster">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:title="胶囊"
|
||||
app:titleTextColor="@android:color/white" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/capsule_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="80dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="暂无速记胶囊"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@android:color/black"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_marginTop="4dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_source"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/text_color_secondary"
|
||||
android:gravity="end"
|
||||
android:layout_marginTop="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,10 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="80dp"
|
||||
android:background="@drawable/capsule_collapsed_bg">
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_menu_rich_text" />
|
||||
</FrameLayout>
|
||||
@ -0,0 +1,48 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/white"
|
||||
android:padding="16dp"
|
||||
android:elevation="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_source"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Source: Unknown"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#888888"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="top|start"
|
||||
android:hint="Type here..."
|
||||
android:background="@null"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cancel"
|
||||
style="?android:attr/buttonBarButtonStyle"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Save"
|
||||
style="?android:attr/buttonBarButtonStyle"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/nav_notes"
|
||||
android:icon="@drawable/ic_menu_notes"
|
||||
android:title="@string/app_name"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_tasks"
|
||||
android:icon="@drawable/ic_menu_tasks"
|
||||
android:title="待办"/>
|
||||
</menu>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeViewTextSelectionChanged|typeWindowContentChanged"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows"
|
||||
android:canRetrieveWindowContent="true"
|
||||
android:description="@string/accessibility_service_description"
|
||||
android:notificationTimeout="100" />
|
||||
@ -0,0 +1,128 @@
|
||||
2026-01-28 18:33:05.844 12829-12829 Compatibil...geReporter net.micode.notes D Compat change id reported: 171979766; UID 10145; state: ENABLED
|
||||
2026-01-28 18:33:06.272 12829-12829 GraphicsEnvironment net.micode.notes V ANGLE Developer option for 'net.micode.notes' set to: 'default'
|
||||
2026-01-28 18:33:06.273 12829-12829 GraphicsEnvironment net.micode.notes V Neither updatable production driver nor prerelease driver is supported.
|
||||
2026-01-28 18:33:06.279 12829-12829 NetworkSecurityConfig net.micode.notes D No Network Security Config specified, using platform default
|
||||
2026-01-28 18:33:06.282 12829-12829 NetworkSecurityConfig net.micode.notes D No Network Security Config specified, using platform default
|
||||
2026-01-28 18:33:06.362 12829-12850 libEGL net.micode.notes D loaded /vendor/lib64/egl/libEGL_emulation.so
|
||||
2026-01-28 18:33:06.364 12829-12850 libEGL net.micode.notes D loaded /vendor/lib64/egl/libGLESv1_CM_emulation.so
|
||||
2026-01-28 18:33:06.369 12829-12850 libEGL net.micode.notes D loaded /vendor/lib64/egl/libGLESv2_emulation.so
|
||||
2026-01-28 18:33:06.420 12829-12829 AppCompatDelegate net.micode.notes D Checking for metadata for AppLocalesMetadataHolderService : Service not found
|
||||
2026-01-28 18:33:06.488 12829-12829 et.micode.note net.micode.notes W Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (unsupported, reflection, allowed)
|
||||
2026-01-28 18:33:06.489 12829-12829 et.micode.note net.micode.notes W Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (unsupported, reflection, allowed)
|
||||
2026-01-28 18:33:06.774 12829-12829 NotesRepository net.micode.notes D NotesRepository initialized
|
||||
2026-01-28 18:33:06.797 12829-12829 NotesRepository net.micode.notes D NotesRepository initialized
|
||||
2026-01-28 18:33:06.801 12829-12829 NotesListViewModel net.micode.notes D ViewModel created
|
||||
2026-01-28 18:33:06.815 12829-12851 FolderListViewModel net.micode.notes D Query executed, cursor: 5
|
||||
2026-01-28 18:33:06.816 12829-12851 FolderListViewModel net.micode.notes D Column names: [_id, parent_id, alert_date, bg_color_id, created_date, has_attachment, modified_date, notes_count, snippet, type, widget_id, widget_type, sync_id, local_modified, origin_parent_id, gtask_id, version, top, locked, title, gtask_priority, gtask_due_date, gtask_status, gtask_finished_time]
|
||||
2026-01-28 18:33:06.818 12829-12851 FolderListViewModel net.micode.notes D Folder data: id=27, name=操作系统, parentId=0, noteCount=1
|
||||
2026-01-28 18:33:06.819 12829-12851 FolderListViewModel net.micode.notes D Folder data: id=19, name=学习, parentId=-4, noteCount=1
|
||||
2026-01-28 18:33:06.822 12829-12851 FolderListViewModel net.micode.notes D Folder data: id=16, name=生活, parentId=-4, noteCount=2
|
||||
2026-01-28 18:33:06.824 12829-12852 NotesRepository net.micode.notes D Successfully loaded sub-folders for folder: 0
|
||||
2026-01-28 18:33:06.825 12829-12851 FolderListViewModel net.micode.notes D Folder data: id=13, name=工作, parentId=-4, noteCount=2
|
||||
2026-01-28 18:33:06.827 12829-12851 FolderListViewModel net.micode.notes D Folder data: id=7, name=软件工程, parentId=0, noteCount=3
|
||||
2026-01-28 18:33:06.827 12829-12851 FolderListViewModel net.micode.notes D QueryAllFolders returned 5 folders
|
||||
2026-01-28 18:33:06.836 12829-12851 FolderListViewModel net.micode.notes D Folder: id=27, name=操作系统, parentId=0
|
||||
2026-01-28 18:33:06.838 12829-12852 NotesListViewModel net.micode.notes D Successfully loaded 18 notes
|
||||
2026-01-28 18:33:06.838 12829-12852 NotesRepository net.micode.notes D Successfully loaded notes for folder: -10
|
||||
2026-01-28 18:33:06.851 12829-12851 FolderListViewModel net.micode.notes D Added root folder: 操作系统
|
||||
2026-01-28 18:33:06.851 12829-12851 FolderListViewModel net.micode.notes D Folder: id=19, name=学习, parentId=-4
|
||||
2026-01-28 18:33:06.851 12829-12851 FolderListViewModel net.micode.notes D Folder: id=16, name=生活, parentId=-4
|
||||
2026-01-28 18:33:06.852 12829-12851 FolderListViewModel net.micode.notes D Folder: id=13, name=工作, parentId=-4
|
||||
2026-01-28 18:33:06.852 12829-12851 FolderListViewModel net.micode.notes D Folder: id=7, name=软件工程, parentId=0
|
||||
2026-01-28 18:33:06.853 12829-12851 FolderListViewModel net.micode.notes D Added root folder: 软件工程
|
||||
2026-01-28 18:33:06.853 12829-12851 FolderListViewModel net.micode.notes D Root folders count: 2
|
||||
2026-01-28 18:33:06.854 12829-12851 FolderListViewModel net.micode.notes D Root expanded: false
|
||||
2026-01-28 18:33:06.854 12829-12851 FolderListViewModel net.micode.notes D Final folder tree size: 2
|
||||
2026-01-28 18:33:06.895 12829-12829 Choreographer net.micode.notes I Skipped 34 frames! The application may be doing too much work on its main thread.
|
||||
2026-01-28 18:33:06.909 12829-12841 System net.micode.notes W A resource failed to call close.
|
||||
2026-01-28 18:33:06.972 12829-12848 HostConnection net.micode.notes D createUnique: call
|
||||
2026-01-28 18:33:07.104 12829-12848 HostConnection net.micode.notes D HostConnection::get() New Host Connection established 0x77af048dc090, tid 12848
|
||||
2026-01-28 18:33:07.110 12829-12848 HostConnection net.micode.notes D HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_native_sync_v2 ANDROID_EMU_native_sync_v3 ANDROID_EMU_native_sync_v4 ANDROID_EMU_dma_v1 ANDROID_EMU_direct_mem ANDROID_EMU_host_composition_v1 ANDROID_EMU_host_composition_v2 ANDROID_EMU_vulkan ANDROID_EMU_deferred_vulkan_commands ANDROID_EMU_vulkan_null_optional_strings ANDROID_EMU_vulkan_create_resources_with_requirements ANDROID_EMU_YUV_Cache ANDROID_EMU_vulkan_ignored_handles ANDROID_EMU_has_shared_slots_host_memory_allocator ANDROID_EMU_vulkan_free_memory_sync ANDROID_EMU_vulkan_shader_float16_int8 ANDROID_EMU_vulkan_async_queue_submit ANDROID_EMU_vulkan_queue_submit_with_commands ANDROID_EMU_sync_buffer_data ANDROID_EMU_vulkan_async_qsri ANDROID_EMU_read_color_buffer_dma ANDROID_EMU_hwc_color_transform GL_OES_EGL_image_external_essl3 GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_host_side_tracing ANDROID_EMU_gles_max_version_3_1
|
||||
2026-01-28 18:33:07.126 12829-12848 OpenGLRenderer net.micode.notes W Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
|
||||
2026-01-28 18:33:07.127 12829-12848 OpenGLRenderer net.micode.notes W Failed to initialize 101010-2 format, error = EGL_SUCCESS
|
||||
2026-01-28 18:33:07.153 12829-12829 NotesRepository net.micode.notes D NotesRepository initialized
|
||||
2026-01-28 18:33:07.196 12829-12852 NotesRepository net.micode.notes D Successfully loaded sub-folders for folder: 0
|
||||
2026-01-28 18:33:07.199 12829-12852 NotesListViewModel net.micode.notes D Successfully loaded 18 notes
|
||||
2026-01-28 18:33:07.200 12829-12852 NotesRepository net.micode.notes D Successfully loaded notes for folder: -10
|
||||
2026-01-28 18:33:07.235 12829-12848 EGL_emulation net.micode.notes D eglCreateContext: 0x77af048dd290: maj 3 min 1 rcv 4
|
||||
2026-01-28 18:33:07.492 12829-12848 EGL_emulation net.micode.notes D eglMakeCurrent: 0x77af048dd290: ver 3 1 (tinfo 0x77b11dbe5080) (first time)
|
||||
2026-01-28 18:33:07.531 12829-12848 Gralloc4 net.micode.notes I mapper 4.x is not supported
|
||||
2026-01-28 18:33:07.534 12829-12848 HostConnection net.micode.notes D createUnique: call
|
||||
2026-01-28 18:33:07.534 12829-12848 HostConnection net.micode.notes D HostConnection::get() New Host Connection established 0x77af048dd7d0, tid 12848
|
||||
2026-01-28 18:33:07.538 12829-12848 goldfish-address-space net.micode.notes D allocate: Ask for block of size 0x100
|
||||
2026-01-28 18:33:07.538 12829-12848 goldfish-address-space net.micode.notes D allocate: ioctl allocate returned offset 0x3edffc000 size 0x2000
|
||||
2026-01-28 18:33:07.544 12829-12848 Gralloc4 net.micode.notes W allocator 4.x is not supported
|
||||
2026-01-28 18:33:07.658 12829-12848 HostConnection net.micode.notes D HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_native_sync_v2 ANDROID_EMU_native_sync_v3 ANDROID_EMU_native_sync_v4 ANDROID_EMU_dma_v1 ANDROID_EMU_direct_mem ANDROID_EMU_host_composition_v1 ANDROID_EMU_host_composition_v2 ANDROID_EMU_vulkan ANDROID_EMU_deferred_vulkan_commands ANDROID_EMU_vulkan_null_optional_strings ANDROID_EMU_vulkan_create_resources_with_requirements ANDROID_EMU_YUV_Cache ANDROID_EMU_vulkan_ignored_handles ANDROID_EMU_has_shared_slots_host_memory_allocator ANDROID_EMU_vulkan_free_memory_sync ANDROID_EMU_vulkan_shader_float16_int8 ANDROID_EMU_vulkan_async_queue_submit ANDROID_EMU_vulkan_queue_submit_with_commands ANDROID_EMU_sync_buffer_data ANDROID_EMU_vulkan_async_qsri ANDROID_EMU_read_color_buffer_dma ANDROID_EMU_hwc_color_transform GL_OES_EGL_image_external_essl3 GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_host_side_tracing ANDROID_EMU_gles_max_version_3_1
|
||||
2026-01-28 18:33:08.276 12829-12829 Choreographer net.micode.notes I Skipped 81 frames! The application may be doing too much work on its main thread.
|
||||
2026-01-28 18:33:08.312 12829-12845 OpenGLRenderer net.micode.notes I Davey! duration=1797ms; Flags=1, FrameTimelineVsyncId=168280, IntendedVsync=13039839683058, Vsync=13040406349702, InputEventId=0, HandleInputStart=13040418543500, AnimationStart=13040418686200, PerformTraversalsStart=13040419832600, DrawStart=13041233560100, FrameDeadline=13039856349724, FrameInterval=13040418003100, FrameStartTime=16666666, SyncQueued=13041257581200, SyncStart=13041428592800, IssueDrawCommandsStart=13041438100800, SwapBuffers=13041789423800, FrameCompleted=13041808230500, DequeueBufferDuration=16100, QueueBufferDuration=880700, GpuCompleted=13041808230500, SwapBuffersCompleted=13041790948300, DisplayPresentTime=0,
|
||||
2026-01-28 18:33:08.540 12829-12838 et.micode.note net.micode.notes I JIT allocated 77KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int)
|
||||
2026-01-28 18:33:08.947 12829-12845 OpenGLRenderer net.micode.notes I Davey! duration=1921ms; Flags=0, FrameTimelineVsyncId=168315, IntendedVsync=13040439683034, Vsync=13041789682980, InputEventId=0, HandleInputStart=13041798356600, AnimationStart=13041798370800, PerformTraversalsStart=13041798600200, DrawStart=13041987454800, FrameDeadline=13040473016366, FrameInterval=13041798273300, FrameStartTime=16666666, SyncQueued=13041989887300, SyncStart=13041989992000, IssueDrawCommandsStart=13041990149900, SwapBuffers=13042273806700, FrameCompleted=13042361397200, DequeueBufferDuration=33100, QueueBufferDuration=382800, GpuCompleted=13042361397200, SwapBuffersCompleted=13042313916500, DisplayPresentTime=0,
|
||||
2026-01-28 18:33:09.888 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=241.80ms min=31.97ms max=647.35ms count=6
|
||||
2026-01-28 18:33:12.865 12829-12858 ProfileInstaller net.micode.notes D Installing profile for net.micode.notes
|
||||
2026-01-28 18:33:13.208 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=518.53ms min=61.05ms max=2682.90ms count=6
|
||||
2026-01-28 18:33:14.315 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=48.21ms min=2.42ms max=89.51ms count=15
|
||||
2026-01-28 18:33:16.533 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=231.10ms min=31.68ms max=1245.57ms count=9
|
||||
2026-01-28 18:33:17.613 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=104.82ms min=7.29ms max=229.11ms count=8
|
||||
2026-01-28 18:33:18.823 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=302.48ms min=126.71ms max=676.01ms count=4
|
||||
2026-01-28 18:33:19.996 12829-12852 NotesListViewModel net.micode.notes D Successfully toggled lock state to true
|
||||
2026-01-28 18:33:19.996 12829-12852 NotesRepository net.micode.notes D Successfully updated lock state for 1 notes
|
||||
2026-01-28 18:33:19.998 12829-12852 NotesRepository net.micode.notes D Successfully loaded sub-folders for folder: 0
|
||||
2026-01-28 18:33:20.001 12829-12852 NotesListViewModel net.micode.notes D Successfully loaded 18 notes
|
||||
2026-01-28 18:33:20.002 12829-12852 NotesRepository net.micode.notes D Successfully loaded notes for folder: -10
|
||||
2026-01-28 18:33:20.008 12829-12852 NotesRepository net.micode.notes D Successfully loaded sub-folders for folder: 0
|
||||
2026-01-28 18:33:20.011 12829-12852 NotesListViewModel net.micode.notes D Successfully loaded 18 notes
|
||||
2026-01-28 18:33:20.015 12829-12852 NotesRepository net.micode.notes D Successfully loaded notes for folder: -10
|
||||
2026-01-28 18:33:20.585 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=579.26ms min=69.15ms max=1150.06ms count=3
|
||||
2026-01-28 18:33:21.989 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=217.92ms min=6.06ms max=925.72ms count=7
|
||||
2026-01-28 18:33:23.885 12829-12829 Compatibil...geReporter net.micode.notes D Compat change id reported: 171228096; UID 10145; state: ENABLED
|
||||
2026-01-28 18:33:24.375 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=274.96ms min=37.73ms max=1773.53ms count=8
|
||||
2026-01-28 18:33:25.462 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=138.04ms min=31.14ms max=528.49ms count=8
|
||||
2026-01-28 18:33:25.561 12829-12829 Compatibil...geReporter net.micode.notes D Compat change id reported: 163400105; UID 10145; state: DISABLED
|
||||
2026-01-28 18:33:25.562 12829-12829 InputMethodManager net.micode.notes D showSoftInput() view=android.widget.EditText{6afc15c VFED..CL. .F.P..ID 0,0-936,124 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
|
||||
2026-01-28 18:33:25.584 12829-12829 AssistStructure net.micode.notes I Flattened final assist data: 6540 bytes, containing 2 windows, 48 views
|
||||
2026-01-28 18:33:26.165 12829-12829 InsetsController net.micode.notes D show(ime(), fromIme=true)
|
||||
2026-01-28 18:33:26.580 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=129.20ms min=47.16ms max=324.82ms count=8
|
||||
2026-01-28 18:33:27.735 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=225.02ms min=26.70ms max=514.93ms count=5
|
||||
2026-01-28 18:33:27.800 12829-12829 InsetsController net.micode.notes D show(ime(), fromIme=true)
|
||||
2026-01-28 18:33:29.265 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=495.39ms min=20.28ms max=1320.45ms count=3
|
||||
2026-01-28 18:33:29.629 12829-12848 OpenGLRenderer net.micode.notes D endAllActiveAnimators on 0x77af8494a510 (RippleDrawable) with handle 0x77ae949a9830
|
||||
2026-01-28 18:33:29.791 12829-12838 et.micode.note net.micode.notes I JIT allocated 77KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int)
|
||||
2026-01-28 18:33:31.635 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=254.05ms min=55.70ms max=974.59ms count=5
|
||||
2026-01-28 18:33:31.899 12829-12852 NotesRepository net.micode.notes D Successfully loaded sub-folders for folder: 0
|
||||
2026-01-28 18:33:31.903 12829-12852 NotesListViewModel net.micode.notes D Successfully loaded 18 notes
|
||||
2026-01-28 18:33:31.903 12829-12852 NotesRepository net.micode.notes D Successfully loaded notes for folder: -10
|
||||
2026-01-28 18:33:32.643 12829-12829 Choreographer net.micode.notes I Skipped 44 frames! The application may be doing too much work on its main thread.
|
||||
2026-01-28 18:33:32.697 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=86.15ms min=38.44ms max=217.87ms count=10
|
||||
2026-01-28 18:33:32.859 12829-12877 OpenGLRenderer net.micode.notes I Davey! duration=835ms; Flags=0, FrameTimelineVsyncId=169346, IntendedVsync=13065423015368, Vsync=13066156348672, InputEventId=0, HandleInputStart=13066165642400, AnimationStart=13066165663300, PerformTraversalsStart=13066165738500, DrawStart=13066166834500, FrameDeadline=13065456348700, FrameInterval=13066165468000, FrameStartTime=16666666, SyncQueued=13066168091300, SyncStart=13066220366400, IssueDrawCommandsStart=13066220448100, SwapBuffers=13066252767600, FrameCompleted=13066310985200, DequeueBufferDuration=27682800, QueueBufferDuration=330100, GpuCompleted=13066285932000, SwapBuffersCompleted=13066310985200, DisplayPresentTime=0,
|
||||
2026-01-28 18:33:32.916 12829-12877 OpenGLRenderer net.micode.notes I Davey! duration=873ms; Flags=0, FrameTimelineVsyncId=169346, IntendedVsync=13065423015368, Vsync=13066156348672, InputEventId=0, HandleInputStart=13066165642400, AnimationStart=13066165663300, PerformTraversalsStart=13066165738500, DrawStart=13066242021400, FrameDeadline=13065456348700, FrameInterval=13066165468000, FrameStartTime=16666666, SyncQueued=13066243836100, SyncStart=13066353178700, IssueDrawCommandsStart=13066353471800, SwapBuffers=13066355119200, FrameCompleted=13066405600900, DequeueBufferDuration=143500, QueueBufferDuration=24341500, GpuCompleted=13066405345300, SwapBuffersCompleted=13066405600900, DisplayPresentTime=0,
|
||||
2026-01-28 18:33:33.958 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=126.48ms min=51.36ms max=248.94ms count=10
|
||||
2026-01-28 18:33:34.959 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=72.41ms min=27.46ms max=168.65ms count=10
|
||||
2026-01-28 18:33:36.123 12829-12848 EGL_emulation net.micode.notes D app_time_stats: avg=1127.39ms min=1127.39ms max=1127.39ms count=1
|
||||
2026-01-28 18:33:36.147 12829-12829 AndroidRuntime net.micode.notes D Shutting down VM
|
||||
2026-01-28 18:33:36.149 12829-12829 AndroidRuntime net.micode.notes E FATAL EXCEPTION: main
|
||||
Process: net.micode.notes, PID: 12829
|
||||
android.content.ActivityNotFoundException: Unable to find explicit activity class {net.micode.notes/net.micode.notes.ui.SettingsActivity}; have you declared this activity in your AndroidManifest.xml?
|
||||
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2085)
|
||||
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1747)
|
||||
at android.app.Activity.startActivityForResult(Activity.java:5404)
|
||||
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:780)
|
||||
at android.app.Activity.startActivityForResult(Activity.java:5362)
|
||||
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:761)
|
||||
at android.app.Activity.startActivity(Activity.java:5748)
|
||||
at android.app.Activity.startActivity(Activity.java:5701)
|
||||
at net.micode.notes.ui.NotesListActivity.onSettingsSelected(NotesListActivity.java:414)
|
||||
at net.micode.notes.ui.SidebarFragment.lambda$setupListeners$6$net-micode-notes-ui-SidebarFragment(SidebarFragment.java:236)
|
||||
at net.micode.notes.ui.SidebarFragment$$ExternalSyntheticLambda2.onClick(D8$$SyntheticClass:0)
|
||||
at android.view.View.performClick(View.java:7441)
|
||||
at android.view.View.performClickInternal(View.java:7418)
|
||||
at android.view.View.access$3700(View.java:835)
|
||||
at android.view.View$PerformClick.run(View.java:28676)
|
||||
at android.os.Handler.handleCallback(Handler.java:938)
|
||||
at android.os.Handler.dispatchMessage(Handler.java:99)
|
||||
at android.os.Looper.loopOnce(Looper.java:201)
|
||||
at android.os.Looper.loop(Looper.java:288)
|
||||
at android.app.ActivityThread.main(ActivityThread.java:7839)
|
||||
at java.lang.reflect.Method.invoke(Native Method)
|
||||
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
|
||||
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
|
||||
---------------------------- PROCESS ENDED (12829) for package net.micode.notes ----------------------------
|
||||
Loading…
Reference in new issue