use external editor, manage permissions
This commit is contained in:
parent
05026b3fe2
commit
34580109bb
4 changed files with 117 additions and 14 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="muzzlevelocity"
|
android:label="muzzlevelocity"
|
||||||
|
|
@ -57,5 +58,8 @@
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"/>
|
<action android:name="android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"/>
|
||||||
</intent>
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:open_filex/open_filex.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'note_editor.dart';
|
import 'note_editor.dart';
|
||||||
|
|
@ -30,6 +32,7 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
List<File> filteredNotes = [];
|
List<File> filteredNotes = [];
|
||||||
String? notesDirectoryPath;
|
String? notesDirectoryPath;
|
||||||
String? defaultDir;
|
String? defaultDir;
|
||||||
|
bool _useExternalEditor = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -45,12 +48,33 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _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<void> _initializeApp() async {
|
Future<void> _initializeApp() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final dir = await getApplicationDocumentsDirectory();
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
setState(() {
|
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) {
|
void _showDirectoryChoiceDialog(String defaultPath) {
|
||||||
|
|
@ -79,6 +103,7 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
await _requestStoragePermission();
|
||||||
Navigator.pop(context); // Close the current dialog
|
Navigator.pop(context); // Close the current dialog
|
||||||
await _selectNotesDirectory(); // Allow user to pick a folder
|
await _selectNotesDirectory(); // Allow user to pick a folder
|
||||||
},
|
},
|
||||||
|
|
@ -194,6 +219,25 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _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) {
|
void _deleteNote(File note) {
|
||||||
final trashDir = Directory('${note.parent.path}/trash');
|
final trashDir = Directory('${note.parent.path}/trash');
|
||||||
if (!trashDir.existsSync()) {
|
if (!trashDir.existsSync()) {
|
||||||
|
|
@ -326,6 +370,14 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
widget.toggleTheme(!widget.isDarkMode);
|
widget.toggleTheme(!widget.isDarkMode);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'External Editor':
|
||||||
|
setState(() {
|
||||||
|
_useExternalEditor = !_useExternalEditor;
|
||||||
|
});
|
||||||
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
prefs.setBool('use_external_editor', _useExternalEditor);
|
||||||
|
});
|
||||||
|
break;
|
||||||
case 'Select Folder':
|
case 'Select Folder':
|
||||||
_showDirectoryChoiceDialog(
|
_showDirectoryChoiceDialog(
|
||||||
notesDirectoryPath ?? ''); // Re-trigger popup
|
notesDirectoryPath ?? ''); // Re-trigger popup
|
||||||
|
|
@ -359,6 +411,21 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
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<String>(
|
PopupMenuItem<String>(
|
||||||
value: 'Select Folder',
|
value: 'Select Folder',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
|
|
@ -410,19 +477,42 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
||||||
children: tagSpans))
|
children: tagSpans))
|
||||||
: null,
|
: null,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await Navigator.push(
|
if (_useExternalEditor) {
|
||||||
context,
|
// Open with system's default editor
|
||||||
MaterialPageRoute(
|
try {
|
||||||
builder: (context) => NoteEditorScreen(
|
if (Platform.isLinux) {
|
||||||
note: File(path.join(notesDirectoryPath!,
|
final result = await Process.run('which', ['xdg-open']);
|
||||||
noteFile.uri.pathSegments.last)),
|
if (result.stdout.toString().trim().isNotEmpty) {
|
||||||
isDarkMode: widget.isDarkMode,
|
await Process.run('xdg-open', [noteFile.path]);
|
||||||
notesDirectoryPath: notesDirectoryPath,
|
} 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
|
||||||
_loadNotes(); // Reload notes after returning
|
setState(() {}); // Force UI refresh
|
||||||
setState(() {}); // Force UI refresh
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
path:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ dependencies:
|
||||||
permission_handler: ^11.4.0
|
permission_handler: ^11.4.0
|
||||||
shared_preferences: ^2.5.2
|
shared_preferences: ^2.5.2
|
||||||
highlight: ^0.7.0
|
highlight: ^0.7.0
|
||||||
|
open_filex: ^4.6.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue