Skip to content

Commit

Permalink
Release v1.2.5
Browse files Browse the repository at this point in the history
  • Loading branch information
Voklen committed May 31, 2023
2 parents d7f8d97 + df5522b commit 174b157
Show file tree
Hide file tree
Showing 14 changed files with 259 additions and 71 deletions.
8 changes: 5 additions & 3 deletions Release checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
- [ ] Copy changelog in to GitHub
- [ ] Attach binaries to GitHub
- [ ] cp build/app/outputs/flutter-apk/app-release.apk daily_diary.apk
- [ ] cp -r build/linux/x64/release/bundle/ linux/
- [ ] tar -czvf linux.tar.gz linux/
- [ ] rm linux/
- [ ] cp -r build/linux/x64/release/bundle/ Daily-Diary/
- [ ] tar -czvf linux.tar.gz Daily-Diary/
- [ ] rm -r Daily-Diary/
- [ ] Publish
- [ ] rm linux.tar.gz daily_diary.apk
- [ ] git checkout main -f
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ android {

defaultConfig {
applicationId "com.voklen.daily_diary"
minSdkVersion flutter.minSdkVersion
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
9 changes: 6 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import 'package:daily_diary/storage.dart';
import 'package:daily_diary/themes.dart';
import 'package:daily_diary/screens/home.dart';

import 'package:shared_preferences/shared_preferences.dart';

// This will be removed when widgets can react to spell check changes
bool? startupCheckSpelling;

String? savePath;
String? startupSavePath;
SavePath? savePath;
SavePath? startupSavePath;

main() async {
savePath = await getPath();
Expand All @@ -29,7 +31,8 @@ main() async {
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);

static final SettingsNotifier settingsNotifier = SettingsNotifier(savePath!);
static final settingsNotifier = SettingsNotifier(savePath!);
static final preferences = SharedPreferences.getInstance();

@override
Widget build(BuildContext context) {
Expand Down
89 changes: 81 additions & 8 deletions lib/path.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,96 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';

import 'package:daily_diary/main.dart';

import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:shared_storage/saf.dart';

class SavePath {
// Due to the constructors only one can ever be null at any time
const SavePath.normal(String this.path) : uri = null;
const SavePath.android(Uri this.uri) : path = null;

final String? path;
final Uri? uri;

bool get isScopedStorage => path == null;

String get string {
if (isScopedStorage) {
String fullString = Uri.decodeFull(uri!.path);
return fullString.split(':').last;
} else {
return path!.replaceFirst('/storage/emulated/0/', '');
}
}

Future<String> getScopedFile(String filename) async {
final file = await getChildFile(filename);
final content = await file.getContentAsString();
return content!;
}

void writeScopedFile(String filename, String content) async {
final file = await getChildFile(filename);
file.writeToFileAsString(content: content);
}

Future<String> getPath() async {
Future<bool> scopedExists(String filename) async {
final scopedStorageFile = await getChildFile(filename);
final exists = await scopedStorageFile.exists();
return exists!;
}

void deleteScoped(String filename) async {
final file = await getChildFile(filename);
file.delete();
}

Future<DocumentFile> getChildFile(String filename) async {
final file = await findFile(uri!, filename);
if (file != null) {
return file;
}
DocumentFile? createdFile =
await createFile(uri!, mimeType: 'text/plain', displayName: filename);
return createdFile!;
}
}

Future<SavePath> getPath() async {
WidgetsFlutterBinding.ensureInitialized();
final preferences = await SharedPreferences.getInstance();
final preferences = await App.preferences;
String? path = preferences.getString('save_path');
bool? isAndroidScoped = preferences.getBool('is_android_scoped');
if (path != null) {
return path;
if (isAndroidScoped == true) {
DocumentFile document = DocumentFile.fromMap(json.decode(path));
return SavePath.android(document.uri);
}
return SavePath.normal(path);
} else {
path = await defaultPath;
preferences.setString('save_path', path);
return path;
return resetPathToDefault();
}
}

Future<String> get defaultPath async {
/// Resets the savePath to default in `SharedPreferences` and returns the
/// `SavePath` it was set to. It does NOT set the global `savePath`.
///
/// To set `savePath` do:
/// ```
/// savePath = await resetPathToDefault();
/// ```
Future<SavePath> resetPathToDefault() async {
final preferences = await App.preferences;
String path = await _defaultPath;
preferences.setString('save_path', path);
preferences.setBool('is_android_scoped', false);
return SavePath.normal(path);
}

Future<String> get _defaultPath async {
final directory = await _directory;
return directory.path;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/screens/previous_entries.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class PreviousEntry extends StatelessWidget {
context,
MaterialPageRoute(
builder: (context) {
String filename = date.toIso8601String().substring(0, 10);
String isoDate = date.toIso8601String().substring(0, 10);
String filename = '$isoDate.txt';
final storage = PreviousEntryStorage(filename, savePath!);
return ViewOnlyScreen(title: humanDate, storage: storage);
},
Expand Down
84 changes: 59 additions & 25 deletions lib/screens/settings.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';

import 'package:file_picker/file_picker.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'package:daily_diary/main.dart';
import 'package:daily_diary/path.dart';

import 'package:file_picker/file_picker.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:shared_storage/shared_storage.dart' as saf;

class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});

Expand Down Expand Up @@ -338,51 +339,84 @@ class SavePathSetting extends StatefulWidget implements SettingTile {

@override
Future<SavePathSetting> newDefault() async {
await setSavePath();
savePath = await resetPathToDefault();
return const SavePathSetting();
}

Future<void> setSavePath() async {
savePath = await defaultPath;
final preferences = await SharedPreferences.getInstance();
preferences.setString('save_path', await defaultPath);
}

@override
State<SavePathSetting> createState() => _SavePathSettingState();
}

class _SavePathSettingState extends State<SavePathSetting> {
_selectNewPath() async {
// Load SharedPreferences while user is picking a path
final preferencesFuture = SharedPreferences.getInstance();
final path = await FilePicker.platform.getDirectoryPath();
final preferences = await preferencesFuture;
void _selectNewPath() async {
final path = await _askForPath();
if (path == null) {
// if the user aborted the dialog or if the folder path couldn't be resolved.
// The user aborted the dialog or the folder path couldn't be resolved.
return;
}
preferences.setString('save_path', path);

setState(() {
savePath = path;
});
}

Future<SavePath?> _askForPath() async {
if (Platform.isAndroid) {
return _askForPathAndroid();
}
String? path = await FilePicker.platform.getDirectoryPath();
if (path == null) {
return null;
}

final preferences = await App.preferences;
preferences.setString('save_path', path);
preferences.setBool('is_android_scoped', false);
return SavePath.normal(path);
}

Future<SavePath?> _askForPathAndroid() async {
_removePreviousPermissions();

// Ask user for path and permissions
Uri? uri;
while (uri == null) {
uri = await saf.openDocumentTree();
}

// Only null before Android API 21, but this project is API 21+
final asDocumentFile = await uri.toDocumentFile();
Map asMap = asDocumentFile!.toMap();
String asString = json.encode(asMap);
final preferences = await App.preferences;
preferences.setString('save_path', asString);
preferences.setBool('is_android_scoped', true);
return SavePath.android(uri);
}

Future<void> _removePreviousPermissions() async {
SavePath? path = savePath;
if (path == null) return;

Uri? previousPath = path.uri;
if (previousPath == null) return;

await saf.releasePersistableUriPermission(previousPath);
}

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Visibility(
visible: Platform.isAndroid,
child: Text(
'Changing this setting will only work properly if the device is rooted:',
style: TextStyle(color: Theme.of(context).colorScheme.primary),
)),
Text(
'Save Location:',
style: Theme.of(context).textTheme.titleMedium,
),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: TextField(
controller: TextEditingController(text: savePath),
controller: TextEditingController(text: savePath!.string),
enabled: false,
style: Theme.of(context).textTheme.bodyMedium,
),
Expand Down
3 changes: 2 additions & 1 deletion lib/settings_notifier.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';

import 'package:daily_diary/path.dart';
import 'package:daily_diary/storage.dart';

class Settings {
Expand All @@ -10,7 +11,7 @@ class Settings {
}

class SettingsNotifier extends ValueNotifier<Settings> {
SettingsNotifier(String savePath)
SettingsNotifier(SavePath savePath)
: storage = SettingsStorage(savePath),
super(Settings());

Expand Down
Loading

0 comments on commit 174b157

Please sign in to comment.