Adding OpenHaystack Mobile app

Co-Authored-By: Lukas Burg <lukas.burg@hemalu.de>
This commit is contained in:
MaxGranzow
2022-05-12 15:28:06 +02:00
committed by Alexander Heinrich
co-authored by Lukas Burg
parent b65a6e6be0
commit 3d593a006c
182 changed files with 10499 additions and 0 deletions
@@ -0,0 +1,57 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:provider/provider.dart';
import 'package:openhaystack_mobile/accessory/accessory_list.dart';
import 'package:openhaystack_mobile/accessory/accessory_registry.dart';
import 'package:openhaystack_mobile/location/location_model.dart';
import 'package:openhaystack_mobile/map/map.dart';
import 'package:latlong2/latlong.dart';
class AccessoryMapListVertical extends StatefulWidget {
final AsyncCallback loadLocationUpdates;
/// Displays a map view and the accessory list in a vertical alignment.
const AccessoryMapListVertical({
Key? key,
required this.loadLocationUpdates,
}) : super(key: key);
@override
State<AccessoryMapListVertical> createState() => _AccessoryMapListVerticalState();
}
class _AccessoryMapListVerticalState extends State<AccessoryMapListVertical> {
final MapController _mapController = MapController();
void _centerPoint(LatLng point) {
_mapController.fitBounds(
LatLngBounds(point),
);
}
@override
Widget build(BuildContext context) {
return Consumer2<AccessoryRegistry, LocationModel>(
builder: (BuildContext context, AccessoryRegistry accessoryRegistry, LocationModel locationModel, Widget? child) {
return Column(
children: [
Flexible(
fit: FlexFit.tight,
child: AccessoryMap(
mapController: _mapController,
),
),
Flexible(
fit: FlexFit.tight,
child: AccessoryList(
loadLocationUpdates: widget.loadLocationUpdates,
centerOnPoint: _centerPoint,
),
),
],
);
},
);
}
}
@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:openhaystack_mobile/accessory/accessory_list.dart';
import 'package:openhaystack_mobile/accessory/accessory_registry.dart';
import 'package:openhaystack_mobile/location/location_model.dart';
import 'package:openhaystack_mobile/map/map.dart';
import 'package:openhaystack_mobile/preferences/preferences_page.dart';
import 'package:openhaystack_mobile/preferences/user_preferences_model.dart';
class DashboardDesktop extends StatefulWidget {
/// Displays the layout for the desktop view of the app.
///
/// The layout is optimized for horizontally aligned larger screens
/// on desktop devices.
const DashboardDesktop({ Key? key }) : super(key: key);
@override
_DashboardDesktopState createState() => _DashboardDesktopState();
}
class _DashboardDesktopState extends State<DashboardDesktop> {
@override
void initState() {
super.initState();
// Initialize models and preferences
var userPreferences = Provider.of<UserPreferences>(context, listen: false);
var locationModel = Provider.of<LocationModel>(context, listen: false);
var locationPreferenceKnown = userPreferences.locationPreferenceKnown ?? false;
var locationAccessWanted = userPreferences.locationAccessWanted ?? false;
if (!locationPreferenceKnown || locationAccessWanted) {
locationModel.requestLocationUpdates();
}
loadLocationUpdates();
}
/// Fetch locaiton updates for all accessories.
Future<void> loadLocationUpdates() async {
var accessoryRegistry = Provider.of<AccessoryRegistry>(context, listen: false);
await accessoryRegistry.loadLocationReports();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
SizedBox(
width: 400,
child: Column(
children: [
AppBar(
title: const Text('OpenHaystack'),
leading: IconButton(
onPressed: () { /* reload */ },
icon: const Icon(Icons.menu),
),
actions: <Widget>[
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const PreferencesPage()),
);
},
icon: const Icon(Icons.settings),
),
],
),
const Padding(
padding: EdgeInsets.all(5),
child: Text('My Accessories')
),
Expanded(
child: AccessoryList(
loadLocationUpdates: loadLocationUpdates,
),
),
],
),
),
const Expanded(
child: AccessoryMap(),
),
],
),
);
}
}
@@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:openhaystack_mobile/accessory/accessory_registry.dart';
import 'package:openhaystack_mobile/dashboard/accessory_map_list_vert.dart';
import 'package:openhaystack_mobile/item_management/item_management.dart';
import 'package:openhaystack_mobile/item_management/new_item_action.dart';
import 'package:openhaystack_mobile/location/location_model.dart';
import 'package:openhaystack_mobile/preferences/preferences_page.dart';
import 'package:openhaystack_mobile/preferences/user_preferences_model.dart';
class DashboardMobile extends StatefulWidget {
/// Displays the layout for the mobile view of the app.
///
/// The layout is optimized for a vertically aligned small screens.
/// The functionality is structured in a bottom tab bar for easy access
/// on mobile devices.
const DashboardMobile({ Key? key }) : super(key: key);
@override
_DashboardMobileState createState() => _DashboardMobileState();
}
class _DashboardMobileState extends State<DashboardMobile> {
/// A list of the tabs displayed in the bottom tab bar.
late final List<Map<String, dynamic>> _tabs = [
{
'title': 'My Accessories',
'body': (ctx) => AccessoryMapListVertical(
loadLocationUpdates: loadLocationUpdates,
),
'icon': Icons.place,
'label': 'Map',
},
{
'title': 'My Accessories',
'body': (ctx) => const KeyManagement(),
'icon': Icons.style,
'label': 'Accessories',
'actionButton': (ctx) => const NewKeyAction(),
},
];
@override
void initState() {
super.initState();
// Initialize models and preferences
var userPreferences = Provider.of<UserPreferences>(context, listen: false);
var locationModel = Provider.of<LocationModel>(context, listen: false);
var locationPreferenceKnown = userPreferences.locationPreferenceKnown ?? false;
var locationAccessWanted = userPreferences.locationAccessWanted ?? false;
if (!locationPreferenceKnown || locationAccessWanted) {
locationModel.requestLocationUpdates();
}
// Load new location reports on app start
loadLocationUpdates();
}
/// Fetch locaiton updates for all accessories.
Future<void> loadLocationUpdates() async {
var accessoryRegistry = Provider.of<AccessoryRegistry>(context, listen: false);
try {
await accessoryRegistry.loadLocationReports();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).colorScheme.error,
content: Text(
'Could not find location reports. Try again later.',
style: TextStyle(
color: Theme.of(context).colorScheme.onError,
),
),
),
);
}
}
/// The selected tab index.
int _selectedIndex = 0;
/// Updates the currently displayed tab to [index].
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My Accessories'),
actions: <Widget>[
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const PreferencesPage()),
);
},
icon: const Icon(Icons.settings),
),
],
),
body: _tabs[_selectedIndex]['body'](context),
bottomNavigationBar: BottomNavigationBar(
items: _tabs.map((tab) => BottomNavigationBarItem(
icon: Icon(tab['icon']),
label: tab['label'],
)).toList(),
currentIndex: _selectedIndex,
selectedItemColor: Theme.of(context).indicatorColor,
onTap: _onItemTapped,
),
floatingActionButton: _tabs[_selectedIndex]['actionButton']?.call(context),
);
}
}