diff --git a/app/lib/api.dart b/app/lib/api.dart deleted file mode 100644 index a7c0548..0000000 --- a/app/lib/api.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; - -import 'package:http/http.dart' as http; -import 'storage.dart'; - -Future createAccount(String signupKey) async { - String serverUrl = (await appStorage.read(key: 'server-url')).toString(); - var uri = Uri.parse('$serverUrl/v0/create-account'); - var response = await http.post( - uri, - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({'signup_key': signupKey}), - ); - debugPrint(response.body); - debugPrint(response.statusCode.toString()); -} - -Future sendLocation(double latitude, double longitude) async { - String serverUrl = (await appStorage.read(key: 'server-url')).toString(); - var uri = Uri.parse('$serverUrl/v0/send-location'); - var response = await http.post( - uri, - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({'lat': latitude, 'lon': longitude}), - ); - debugPrint(response.body); - debugPrint(response.statusCode.toString()); -} diff --git a/app/lib/backgroundservices.dart b/app/lib/backgroundservices.dart deleted file mode 100644 index b724fa0..0000000 --- a/app/lib/backgroundservices.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Move stuff here later. -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/material.dart'; -import 'package:flutter_background_service/flutter_background_service.dart'; - -// Local imports -import 'LocationService.dart'; -import 'api.dart' as api; diff --git a/app/lib/main.dart b/app/lib/main.dart index 14e41f2..6107913 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -2,12 +2,13 @@ import 'package:flutter/material.dart'; import 'dart:ui'; import 'dart:async'; +import 'dart:convert'; // Local imports //import 'backgroundservices.dart' as background_services; -import 'storage.dart'; -import 'api.dart' as api; -import 'LocationService.dart'; +import 'utils/storage.dart'; +import 'utils/api.dart' as api; +//import 'utils/time.dart'; // Background service import 'package:flutter_background_service/flutter_background_service.dart'; @@ -23,11 +24,28 @@ Future main() async { value: 'http://192.168.68.103:3000', ); - //await background_services.startBackgroundLocationService(); + await appStorage.write(key: 'viewers', value: '{}'); + + debugPrint('viewers: ${await appStorage.read(key: "viewers")}'); + + final original = (await appStorage.read(key: 'viewers')).toString(); + dynamic temp = jsonDecode(original); + + final json = '{"viewer_id": "viewernumxxx"}'; // This would be the response + final viewerId = jsonDecode(json)['viewer_id']; + + temp[viewerId] = 'active'; + + final jsonNew = jsonEncode(temp); + + await appStorage.write(key: 'viewers', value: jsonNew); + + debugPrint('viewers: ${await appStorage.read(key: "viewers")}'); + await startBackgroundLocationService(); startBackgroundService(); - //api.createAccount('CzS6H'); + await api.sendPings(); runApp(const MainApp()); } @@ -87,15 +105,12 @@ Future onIosBackground(ServiceInstance service) async { @pragma('vm:entry-point') void onStart(ServiceInstance service) async { - double counter = 0.0; Timer.periodic(const Duration(seconds: 1), (timer) async { - await LocationUtils.updatePosition(); + /*await LocationUtils.updatePosition(); await api.sendLocation( LocationUtils.getLatitude(), LocationUtils.getLongitude(), - ); - //await api.sendLocation(counter, counter); - counter += 0.1; + );*/ }); } diff --git a/app/lib/storage.dart b/app/lib/storage.dart deleted file mode 100644 index c032cb3..0000000 --- a/app/lib/storage.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - -final appStorage = FlutterSecureStorage(); - -/* - -Write Data -await appStorage.write(key: 'server-url', value: '192.168.68.103:3000'); - -Read Data -String? username = await appStorage.read(key: 'server-url'); - -Delete Data -await appStorage.delete(key: 'server-url'); - -Delete All Data -await appStorage.deleteAll(); - -Check for Key Existence -bool containsKey = await appStorage.containsKey(key: 'server-url'); - -*/ diff --git a/app/lib/LocationService.dart b/app/lib/utils/LocationService.dart similarity index 100% rename from app/lib/LocationService.dart rename to app/lib/utils/LocationService.dart diff --git a/app/lib/utils/api.dart b/app/lib/utils/api.dart new file mode 100644 index 0000000..e0d8563 --- /dev/null +++ b/app/lib/utils/api.dart @@ -0,0 +1,247 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import 'package:http/http.dart' as http; +import 'storage.dart'; +import 'crypto.dart' as crypto; + +// + +// Example API function: +Future exampleAPIFunction() async { + // Replace the function name and return type to match the actual API needs. + if (appStorage.read(key: 'account_created').toString() != 'true') { + return 'No account'; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + // Replace path with the actual endpoint. + var uri = Uri.parse('$serverUrl/v0/path'); + var response = await http.post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + body: jsonEncode({}), // Replace with the actual request body + ); + + if (response.statusCode == 200) { + return jsonDecode( + response.body, + )['signup_key']; // Replace with the actual response field; If it's a bool return false. + } + + return '${response.statusCode} ${response.body}'; // This is just to return the error; If it's a bool return false. +} + +// + +// API functions from ```privacypin/API.md``` +Future createAccount(String signupKey) async { + // returns true if the account was created successfully, false otherwise, and stores it in appStorage. + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/create-account'); + var response = await http.post( + uri, + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({'signup_key': signupKey}), + ); + + if (response.statusCode == 200) { + appStorage.write( + key: 'user_id', + value: jsonDecode(response.body)['user_id'], + ); + appStorage.write(key: 'token', value: jsonDecode(response.body)['token']); + appStorage.write( + key: 'is_admin', + value: jsonDecode(response.body)['is_admin'], + ); + appStorage.write(key: 'account_created', value: 'true'); + appStorage.write(key: 'viewers', value: jsonEncode({})); + return true; + } + + // This code would execute if the response status code is not 200(meaning the request was not successful) + debugPrint('!!!!! ${response.statusCode} ${response.body}'); + return false; +} + +Future generateSignupKey() async { + if (appStorage.read(key: 'account_created').toString() != 'true') { + return 'No token'; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/generate-signup-key'); + var response = await http.post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + body: jsonEncode({}), + ); + + if (response.statusCode == 200) { + return jsonDecode(response.body)['signup_key']; + } + + return '${response.statusCode} ${response.body}'; +} + +Future createInvite() async { + // This returns the token and the unix timestamp; I'm not sure if the token needs to be stored or not so I didn't store it + if (appStorage.read(key: 'account_created').toString() != 'true') { + return 'No account'; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/create-invite'); + var response = await http.post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + body: jsonEncode({}), + ); + + if (response.statusCode == 200) { + return response.body; + } + + return '${response.statusCode} ${response.body}'; +} + +Future acceptInvite(String inviteToken) async { + // This function adds the viewer_id to the viewers list and returns the viewer_id of the accepted invite + if (appStorage.read(key: 'account_created').toString() != 'true') { + return 'No account'; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/accept-invite'); + var response = await http.post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + body: jsonEncode({'invite_token': inviteToken}), + ); + + if (response.statusCode == 200) { + await addItemToJsonStorage( + 'viewers', + jsonDecode(response.body)['viewer_id'], + 'some encryption key?', + ); + return jsonDecode(response.body)['viewer_id']; + } + + return '${response.statusCode} ${response.body}'; +} + +Future sendPings() async { + // This function sends pings to the server for each viewer_id in the viewers list + if (appStorage.read(key: 'account_created').toString() != 'true') { + return false; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/send-pings'); + var response = await http.post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + body: await crypto.getPings(), + ); + + if (response.statusCode == 200) { + return true; + } + + return false; +} + +Future getPings(String viewerId) async { + if (appStorage.read(key: 'account_created').toString() != 'true') { + return 'No account'; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/get-pings'); + var response = await http.post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + body: jsonEncode({'sender_id': viewerId}), + ); + + if (response.statusCode == 200) { + return response.body; + } + + return '${response.statusCode} ${response.body}'; +} + +Future getEvents(String viewerId) async { + if (appStorage.read(key: 'account_created').toString() != 'true') { + return 'No account'; + } + + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/events'); + var response = await http.get( + uri, + headers: { + 'Content-Type': 'application/json', + 'Authorization': + 'Bearer ${(await appStorage.read(key: 'token')).toString()}', + 'X-User-Id': (await appStorage.read(key: 'user_id')).toString(), + }, + ); + + if (response.statusCode == 200) { + return response.body; + } + + return '${response.statusCode} ${response.body}'; +} + +// + +// + +// Testing functions +Future sendLocation(double latitude, double longitude) async { + String serverUrl = (await appStorage.read(key: 'server-url')).toString(); + var uri = Uri.parse('$serverUrl/v0/send-location'); + var response = await http.post( + uri, + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({'lat': latitude, 'lon': longitude}), + ); + + debugPrint('!!!!! ${response.body}'); + debugPrint('!!!!! ${response.statusCode.toString()}'); +} diff --git a/app/lib/utils/crypto.dart b/app/lib/utils/crypto.dart new file mode 100644 index 0000000..b10061c --- /dev/null +++ b/app/lib/utils/crypto.dart @@ -0,0 +1,19 @@ +Future getPings() async { + /* + + @azom.dev's encryption algorithm needs to be fully made first, so it will be implemented later. + The blob will look like this: + + [ + {viewer_id: id0, encrypted_blob: blob0}, + {viewer_id: id1, encrypted_blob: blob1}, + {viewer_id: id2, encrypted_blob: blob2}, + ] + + */ + return '[\n {\'viewer_id\': \'id\', \'encrypted_blob\': blob0},\n {\'viewer_id\': \'id\', \'encrypted_blob\': blob1},\n {\'viewer_id\': \'id\', \'encrypted_blob\': blob2},\n]'; +} + +void convertPings(String senderId, String pings) { + // To implement later +} diff --git a/app/lib/utils/storage.dart b/app/lib/utils/storage.dart new file mode 100644 index 0000000..2e0690c --- /dev/null +++ b/app/lib/utils/storage.dart @@ -0,0 +1,44 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'dart:convert'; + +final appStorage = FlutterSecureStorage(); + +/* + +Write Data +await appStorage.write(key: 'server-url', value: '192.168.68.103:3000'); + +Read Data +String? username = await appStorage.read(key: 'server-url'); + +Delete Data +await appStorage.delete(key: 'server-url'); + +Delete All Data +await appStorage.deleteAll(); + +Check for Key Existence +bool containsKey = await appStorage.containsKey(key: 'server-url'); + +*/ + +// Custom functions +Future addItemToJsonStorage( + String storagekey, + dynamic jsonkey, + dynamic jsonvalue, +) async { + final original = (await appStorage.read(key: storagekey)).toString(); + dynamic temp = jsonDecode(original); + temp[jsonkey] = jsonvalue; + final jsonNew = jsonEncode(temp); + await appStorage.write(key: storagekey, value: jsonNew); +} + +Future addLocationInfo( + String viewer, + int timestamp, + String latlongJson, +) async { + final original = (await appStorage.read(key: 'viewers')).toString(); +} diff --git a/app/lib/utils/time.dart b/app/lib/utils/time.dart new file mode 100644 index 0000000..6460732 --- /dev/null +++ b/app/lib/utils/time.dart @@ -0,0 +1,11 @@ +class time { + static int getCurrentUnixTime() { + // Returns seconds + return DateTime.now().millisecondsSinceEpoch ~/ 1000; + } + + static int getTimeUntil(int unixTime) { + // Returns seconds + return unixTime - getCurrentUnixTime(); + } +} diff --git a/app/test/widget_test.dart b/app/test/widget_test.dart index adf2065..b272fcc 100644 --- a/app/test/widget_test.dart +++ b/app/test/widget_test.dart @@ -5,7 +5,7 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; +/*import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:privacypin/main.dart'; @@ -27,4 +27,4 @@ void main() { expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); -} + }*/ diff --git a/justfile b/justfile new file mode 100644 index 0000000..3fa774c --- /dev/null +++ b/justfile @@ -0,0 +1,10 @@ +alias a := app +alias qad := quick_app_debug + +# Use this one for normal testing. This is required for the background service to work. +app: + cd app && flutter clean && flutter pub get && flutter run --release + +# Use this one for quick app debugging mostly for a one time test of an api call. +quick_app_debug: + cd app && flutter run