diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index ba0b42b..9da57ca 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
+
+
+
+
diff --git a/lib/note_list.dart b/lib/note_list.dart
index ca334e0..4a461d8 100644
--- a/lib/note_list.dart
+++ b/lib/note_list.dart
@@ -2,7 +2,9 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:google_fonts/google_fonts.dart';
+import 'package:open_filex/open_filex.dart';
import 'package:path/path.dart' as path;
+import 'package:permission_handler/permission_handler.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'note_editor.dart';
@@ -30,6 +32,7 @@ class _NoteListScreenState extends State {
List filteredNotes = [];
String? notesDirectoryPath;
String? defaultDir;
+ bool _useExternalEditor = false;
@override
void initState() {
@@ -45,12 +48,33 @@ class _NoteListScreenState extends State {
});
}
+ Future _requestStoragePermission() async {
+ if (Platform.isAndroid) {
+ if (await Permission.storage.request().isGranted) {
+ print("✅ Basic storage permission granted.");
+ return;
+ }
+
+ if (await Permission.manageExternalStorage.request().isGranted) {
+ print("✅ Full file access granted.");
+ return;
+ }
+
+ print("❌ Storage permission denied. Opening settings...");
+ await openAppSettings(); // Open settings if denied
+ }
+ }
+
Future _initializeApp() async {
+ final prefs = await SharedPreferences.getInstance();
final dir = await getApplicationDocumentsDirectory();
+
setState(() {
- defaultDir = dir.path; // Update state with defaultDir
+ defaultDir = dir.path;
+ _useExternalEditor = prefs.getBool('use_external_editor') ?? false;
});
- await _loadNotes(); // Load notes after setting defaultDir
+
+ await _loadNotes();
}
void _showDirectoryChoiceDialog(String defaultPath) {
@@ -79,6 +103,7 @@ class _NoteListScreenState extends State {
),
TextButton(
onPressed: () async {
+ await _requestStoragePermission();
Navigator.pop(context); // Close the current dialog
await _selectNotesDirectory(); // Allow user to pick a folder
},
@@ -194,6 +219,25 @@ class _NoteListScreenState extends State {
.trim();
}
+ Future _openExternalEditor(File noteFile) async {
+ if (!noteFile.existsSync()) {
+ print("❌ File does not exist: ${noteFile.path}");
+ return;
+ }
+
+ try {
+ if (Platform.isAndroid) {
+ await _requestStoragePermission();
+ }
+
+ final result = await OpenFilex.open(noteFile.path);
+ print("✅ OpenFilex result: ${result.type}, message: ${result.message}");
+ } catch (e) {
+ print("❌ Failed to open external editor: $e");
+ }
+ }
+
+
void _deleteNote(File note) {
final trashDir = Directory('${note.parent.path}/trash');
if (!trashDir.existsSync()) {
@@ -326,6 +370,14 @@ class _NoteListScreenState extends State {
widget.toggleTheme(!widget.isDarkMode);
});
break;
+ case 'External Editor':
+ setState(() {
+ _useExternalEditor = !_useExternalEditor;
+ });
+ SharedPreferences.getInstance().then((prefs) {
+ prefs.setBool('use_external_editor', _useExternalEditor);
+ });
+ break;
case 'Select Folder':
_showDirectoryChoiceDialog(
notesDirectoryPath ?? ''); // Re-trigger popup
@@ -359,6 +411,21 @@ class _NoteListScreenState extends State {
),
),
),
+ PopupMenuItem(
+ value: 'External Editor',
+ child: ListTile(
+ leading: Icon(
+ _useExternalEditor
+ ? Icons.open_in_new_off
+ : Icons.open_in_new,
+ ),
+ title: Text(
+ !_useExternalEditor
+ ? 'Use External Editor'
+ : 'Use Internal Editor',
+ ),
+ ),
+ ),
PopupMenuItem(
value: 'Select Folder',
child: ListTile(
@@ -410,19 +477,42 @@ class _NoteListScreenState extends State {
children: tagSpans))
: null,
onTap: () async {
- await Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => NoteEditorScreen(
- note: File(path.join(notesDirectoryPath!,
- noteFile.uri.pathSegments.last)),
- isDarkMode: widget.isDarkMode,
- notesDirectoryPath: notesDirectoryPath,
+ if (_useExternalEditor) {
+ // Open with system's default editor
+ try {
+ if (Platform.isLinux) {
+ final result = await Process.run('which', ['xdg-open']);
+ if (result.stdout.toString().trim().isNotEmpty) {
+ await Process.run('xdg-open', [noteFile.path]);
+ } else {
+ print("❌ `xdg-open` is not available.");
+ }
+ } else if (Platform.isAndroid) {
+ await _requestStoragePermission();
+ final result = await OpenFilex.open(
+ noteFile.path,
+ type: "text/markdown",
+ );
+ print("✅ OpenFilex result: ${result.type}, message: ${result.message}");
+ }
+ } catch (e) {
+ print("❌ Failed to open external editor: $e");
+ }
+ } else {
+ // Open internally
+ await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => NoteEditorScreen(
+ note: File(path.join(notesDirectoryPath!, noteFile.uri.pathSegments.last)),
+ isDarkMode: widget.isDarkMode,
+ notesDirectoryPath: notesDirectoryPath,
+ ),
),
- ),
- );
- _loadNotes(); // Reload notes after returning
- setState(() {}); // Force UI refresh
+ );
+ _loadNotes(); // Reload notes after returning
+ setState(() {}); // Force UI refresh
+ }
},
),
);
diff --git a/pubspec.lock b/pubspec.lock
index e599e15..c89ff32 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -336,6 +336,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
+ open_filex:
+ dependency: "direct main"
+ description:
+ name: open_filex
+ sha256: dcb7bd3d32db8db5260253a62f1564c02c2c8df64bc0187cd213f65f827519bd
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.6.0"
path:
dependency: "direct main"
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 3e5f11c..a8d1a00 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -46,6 +46,7 @@ dependencies:
permission_handler: ^11.4.0
shared_preferences: ^2.5.2
highlight: ^0.7.0
+ open_filex: ^4.6.0
dev_dependencies:
flutter_test: