Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Location trigger#84 #148

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<queries>
<intent>
<action android:name="android.speech.RecognitionService" />
Expand Down
18 changes: 10 additions & 8 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ PODS:
- fluttertoast (0.0.2):
- Flutter
- Toast
- geolocator_apple (1.2.0):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
Expand All @@ -66,6 +68,7 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
Expand All @@ -92,6 +95,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
Expand All @@ -109,14 +114,11 @@ SPEC CHECKSUMS:
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
speech_to_text: 9dc43a5df3cbc2813f8c7cc9bd0fbf94268ed7ac
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
speech_to_text: 627d3fd2194770b51abb324ba45c2d39398f24a8
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96

Expand Down
4 changes: 3 additions & 1 deletion ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@
4CD6A70718A37A05253E6E62 /* Pods-RunnerTests.release.xcconfig */,
53896B7435ECC0E20EF3D9B6 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
Expand Down Expand Up @@ -490,6 +489,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down Expand Up @@ -615,6 +615,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down Expand Up @@ -672,6 +673,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down
4 changes: 4 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>

</dict>
</plist>
10 changes: 10 additions & 0 deletions lib/models/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class Task {
bool get isRecurring => recurringDays != null;

bool get hasDeadline => deadline != null;
double? lat;
double? lng;

bool get hasLocationAttached => lat != null && lng != null;

Task({
required this.title,
Expand All @@ -27,6 +31,8 @@ class Task {
this.dependency,
String? id,
List<Subtask>? subtasks,
this.lat,
this.lng,
}) : id = id ?? DateTime.now().microsecondsSinceEpoch.toString(),
subtasks = subtasks ?? [];

Expand All @@ -40,6 +46,8 @@ class Task {
'dependency': dependency?.toJson(),
'recurringDays': recurringDays,
'color': color.value,
'lat': lat,
'lng': lng,
'id': id,
'subtasks': subtasks.map((e) => e.toMap()).toList(),
};
Expand All @@ -64,6 +72,8 @@ class Task {
json['dependency'] != null ? Task.fromJson(json['dependency']) : null,
recurringDays: json['recurringDays'],
color: Color(json['color']),
lat: json['lat'],
lng: json['lng'],
id: json['id'] ?? DateTime.now().millisecondsSinceEpoch.toString(),
subtasks: subtasks,
);
Expand Down
45 changes: 36 additions & 9 deletions lib/screens/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:geolocator/geolocator.dart';
import 'package:taskly/constants.dart';
import 'package:taskly/enums/taskoptions.dart';
import 'package:taskly/service/permissions_service.dart';
import 'package:taskly/service/location_service.dart';
import 'package:taskly/storage/kudos_storage.dart';
import 'package:taskly/models/kudos.dart';
import 'package:taskly/models/tip.dart';
Expand All @@ -30,6 +32,8 @@ class HomeScreen extends StatefulWidget {
}

class _HomeScreenState extends State<HomeScreen> {
static const int geofenceRadius = 50;

static const platform = MethodChannel('com.example.taskly/deeplink');
List<Task> tasks = [];
Kudos kudos = Kudos(score: 0, history: []);
Expand All @@ -43,9 +47,28 @@ class _HomeScreenState extends State<HomeScreen> {
_fetch();
_loadTasks();
_loadKudos();
_checkCurrentLocation();
_listenForDeepLink();
}

void _checkCurrentLocation() async {
Position? current = await LocationService.getCurrentLocation();
if (current == null) return;

List<Task> completed = tasks
.where((element) =>
element.hasLocationAttached &&
!element.isCompleted &&
LocationService.getDistance(current.latitude, current.longitude,
element.lat!, element.lng!) <
geofenceRadius)
.toList();

for (Task item in completed) {
await _toggleTaskCompletion(tasks.indexOf(item), true);
}
}

void _listenForDeepLink() {
platform.setMethodCallHandler(
(call) async {
Expand Down Expand Up @@ -98,7 +121,7 @@ class _HomeScreenState extends State<HomeScreen> {
await TaskStorage.saveTasks(tasks);
}

void _toggleTaskCompletion(int index, bool? value) async {
Future _toggleTaskCompletion(int index, bool? value) async {
if (tasks[index].dependency != null &&
!tasks[index].dependency!.isCompleted) {
Fluttertoast.showToast(
Expand Down Expand Up @@ -156,11 +179,12 @@ class _HomeScreenState extends State<HomeScreen> {
await TaskStorage.saveTasks(tasks);
}

void kudosForMeditation(int scoreChange, String mssg) async{
kudos.score += scoreChange;
kudos.history.add([mssg, scoreChange.toString()]);
await KudosStorage.saveKudos(kudos);
}
void kudosForMeditation(int scoreChange, String mssg) async {
kudos.score += scoreChange;
kudos.history.add([mssg, scoreChange.toString()]);
await KudosStorage.saveKudos(kudos);
}

// Handle task options, now using the enum
void _onOptionSelected(TaskOption option) {
setState(() {
Expand Down Expand Up @@ -190,13 +214,16 @@ void kudosForMeditation(int scoreChange, String mssg) async{
TaskStorage.saveTasks(tasks);
});
} else if (option == TaskOption.launchMeditationScreen) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => MeditationScreen(kudosForMeditation: kudosForMeditation)));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MeditationScreen(kudosForMeditation: kudosForMeditation)));
} else if (option == TaskOption.toggleTipVisibility) {
showtip = !showtip;
} else if (option == TaskOption.exportToCSV) {
exportToCSV(tasks);
} else if (option == TaskOption.defaultColor){
} else if (option == TaskOption.defaultColor) {
showColorPickerDialog(context);
}
});
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/task_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class _TaskBoxWidgetState extends State<TaskBoxWidget> {
),
IconButton(
icon: const Icon(Icons.share_rounded),
onPressed: onShare,
onPressed: widget.onShare,
),
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
Expand Down
34 changes: 34 additions & 0 deletions lib/screens/taskform_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:taskly/models/subtask.dart';
import 'package:geolocator/geolocator.dart';
import 'package:taskly/models/task.dart';
import 'package:taskly/service/location_service.dart';
import 'package:taskly/service/speech_service.dart';
import 'package:taskly/constants.dart';
import 'package:taskly/storage/task_storage.dart';
Expand Down Expand Up @@ -38,6 +40,7 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
Color defaultColor = Colors.blue;
late List<Task> _availableTasks;
late List<Subtask> subtasks;
double? lat, lng;

bool isTitleListening = false;

Expand Down Expand Up @@ -67,6 +70,8 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
if (hasDeadline) deadline = widget.task?.deadline;
subtasks = widget.task?.subtasks ?? [];
setSelectedColour();
lat = widget.task?.lat;
lng = widget.task?.lng;
}

@override
Expand Down Expand Up @@ -376,6 +381,33 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
},
),
),
const Spacing(),
ElevatedButton(
onPressed: lat != null && lng != null
? null
: () async {
Position? position = await LocationService.getCurrentLocation();
if (position == null) return;

setState(() {
Fluttertoast.showToast(
msg:
"Task will be auto-completed when you are again at this location");
lat = position.latitude;
lng = position.longitude;
});
},
child: const Text("Autocomplete task at current location"),
),
if (lat != null && lng != null)
ElevatedButton(
onPressed: () {
setState(() {
lat = lng = null;
});
},
child: const Text("Detach location from task"),
),
];
}

Expand All @@ -391,6 +423,8 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
color: selectedColor ?? defaultColor,
dependency: selectedDependency,
subtasks: subtasks,
lat: lat,
lng: lng,
);
Fluttertoast.showToast(
msg: editing
Expand Down
18 changes: 12 additions & 6 deletions lib/screens/tasklist_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,18 @@ class _TaskListScreenState extends State<TaskListScreen> {
}
},
onLongPress: () => _toggleTaskSelection(index),
title: Text(
task.title,
style: TextStyle(
decoration:
task.isCompleted ? TextDecoration.lineThrough : null,
),
title: Row(
children: [
Text(
task.title,
style: TextStyle(
decoration:
task.isCompleted ? TextDecoration.lineThrough : null,
),
),
if (task.hasLocationAttached)
const Icon(Icons.pin_drop_outlined)
],
),
subtitle: Text(
task.description.length > 30
Expand Down
16 changes: 16 additions & 0 deletions lib/service/location_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:geolocator/geolocator.dart';
import 'package:taskly/service/permission_service.dart';

class LocationService {
static Future<Position?> getCurrentLocation() async {
bool granted = await PermissionService.askForLocation();
if (!granted) return null;

return Geolocator.getCurrentPosition();
}

static double getDistance(
double lat1, double lng1, double lat2, double lng2) {
return Geolocator.distanceBetween(lat1, lng1, lat2, lng2);
}
}
28 changes: 28 additions & 0 deletions lib/service/permission_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:fluttertoast/fluttertoast.dart';
import 'package:geolocator/geolocator.dart';

class PermissionService {
static Future<bool> askForLocation() async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
Fluttertoast.showToast(msg: "Enable location services.");
return false;
}

LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
Fluttertoast.showToast(
msg: "Grant location permission to use this feature");
return false;
}
} else if (permission == LocationPermission.deniedForever) {
Fluttertoast.showToast(
msg: "Enable location permissions by going to Settings/Apps/Taskly");
return false;
}

return true;
}
}
Loading