Commit 30e4190f authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬

Added mock data to main UI, fixed renderflex overflow in settings after language upgrade

parent 8633933b
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-08-18 17:56:19.621785","version":"1.21.0-10.0.pre.139"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-08-20 22:54:47.792457","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
......@@ -98,6 +98,13 @@ class DataHandler {
}
}
Stream<DataPacket> getMockDataStream() async* {
while(true) {
yield new DataPacket.fromData(1597957282000, 5, 42, 420, 3.0, 32, 24, 10123, 32, null, 5, null, 9, "");
await Future.delayed(Duration(milliseconds: 500));
}
}
void dispose() {
DataHeader().dispose();
this._data.removeWhere((element) => true);
......
......@@ -23,7 +23,8 @@ class CarouselCard extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(4)),
),
child: SingleChildScrollView(
child: child
clipBehavior: Clip.antiAlias,
child: child,
),
),
),
......
......@@ -42,7 +42,7 @@ class _SettingsFormState extends State<SettingsForm> {
@override
void dispose() {
// Clean up the controller when the widget is disposed.
// Clean up the controllers when the widget is disposed.
_bleServiceController.dispose();
_bleCharacteristicController.dispose();
_mapQueryLimitController.dispose();
......@@ -53,147 +53,142 @@ class _SettingsFormState extends State<SettingsForm> {
@override
Widget build(BuildContext context) {
/// The [Form] is comprised of multiple [ExpansionTile]s that represent settings subsections from general app management to more advanced settings.
return BaseWidget(
builder: (context, sizingInfo) => Form(
key: _formKey,
child: Column(
children: [
SizedBox(
height: sizingInfo.screenSize.height * 0.8,
child: SingleChildScrollView(
child: Column(
children: [
// NETWORKING
ExpansionTile(
title: Text('Network'),
initiallyExpanded: true,
leading: Icon(Icons.network_check),
children: [
ListTile(title: Text('TODO: Max number of items per push (def 100)')),
ListTile(title: Text('TODO: Push Frequency')),
ListTile(title: Text('TODO: Push on 3G/4G')),
],
),
Divider(color: Colors.black,),
// CACHING
ExpansionTile(
title: Text('Local Data'),
initiallyExpanded: true,
leading: Icon(Icons.storage),
children: [
_buildActionTile('Delete local data\nOperation is final', () => DatabaseHandler().recreateDB())
],
),
Divider(color: Colors.black,),
// BLUETOOTH
ExpansionTile(
title: Text('Bluetooth'),
subtitle: Text('(Advanced)'),
leading: Icon(Icons.bluetooth),
children: [
CheckboxListTile(
title: Text('Modify advanced bluetooth settings'),
value: _advancedBTOptions ?? false,
onChanged: (newValue) => setState(() {
_advancedBTOptions = newValue;
}),
controlAffinity: ListTileControlAffinity.leading,
),
Visibility(
visible: _advancedBTOptions ?? false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Changing these settings can break device data acquisition',
style: TextStyle(
color: Colors.red[600],
fontSize: 12
),
textAlign: TextAlign.center,
return Form(
key: _formKey,
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
// NETWORKING
ExpansionTile(
title: Text('Network'),
initiallyExpanded: true,
leading: Icon(Icons.network_check),
children: [
ListTile(title: Text('TODO: Max number of items per push (def 100)')),
ListTile(title: Text('TODO: Push Frequency')),
ListTile(title: Text('TODO: Push on 3G/4G')),
],
),
Divider(color: Colors.black,),
// CACHING
ExpansionTile(
title: Text('Local Data'),
initiallyExpanded: true,
leading: Icon(Icons.storage),
children: [
_buildActionTile('Delete local data\nOperation is final', () => DatabaseHandler().recreateDB())
],
),
Divider(color: Colors.black,),
// BLUETOOTH
ExpansionTile(
title: Text('Bluetooth'),
subtitle: Text('(Advanced)'),
leading: Icon(Icons.bluetooth),
children: [
CheckboxListTile(
title: Text('Modify advanced bluetooth settings'),
value: _advancedBTOptions ?? false,
onChanged: (newValue) => setState(() {
_advancedBTOptions = newValue;
}),
controlAffinity: ListTileControlAffinity.leading,
),
Visibility(
visible: _advancedBTOptions ?? false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Changing these settings can break device data acquisition',
style: TextStyle(
color: Colors.red[600],
fontSize: 12
),
textAlign: TextAlign.center,
),
),
// TODO Service / Characteristic Explorer ?
_buildInputTile(_bleServiceController, 'BLE Service UUID', 'BT.SERVICE_UUID', _advancedBTOptions, TextInputType.text, <TextInputFormatter>[ ], RegExp('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')),
//_buildInputTile(_bleCharacteristicController, 'BLE Characteristic UUID', 'BT.CHARACTERISTIC_UUID', _advancedBTOptions, TextInputType.text, <TextInputFormatter>[ ], RegExp('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')),
],
),
Divider(color: Colors.black,),
// MAP
ExpansionTile(
title: Text('Map'),
subtitle: Text('(Advanced)'),
leading: Icon(Icons.map),
children: [
CheckboxListTile(
title: Text('Modify advanced map settings'),
value: _advancedMapOptions ?? false,
onChanged: (newValue) {
Future.delayed(Duration.zero, () => {
if (newValue != _advancedMapOptions) setState(() {
_advancedMapOptions = newValue;
})
});
},
controlAffinity: ListTileControlAffinity.leading,
),
Visibility(
visible: _advancedMapOptions ?? false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Changing these settings can have serious performance impacts on your device and battery life',
style: TextStyle(
color: Colors.red[600],
fontSize: 12
),
textAlign: TextAlign.center,
),
// TODO Service / Characteristic Explorer ?
_buildInputTile(_bleServiceController, 'BLE Service UUID', 'BT.SERVICE_UUID', _advancedBTOptions, TextInputType.text, <TextInputFormatter>[ ], RegExp('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')),
_buildInputTile(_bleCharacteristicController, 'BLE Characteristic UUID', 'BT.CHARACTERISTIC_UUID', _advancedBTOptions, TextInputType.text, <TextInputFormatter>[ ], RegExp('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')),
],
),
Divider(color: Colors.black,),
// MAP
ExpansionTile(
title: Text('Map'),
subtitle: Text('(Advanced)'),
leading: Icon(Icons.map),
children: [
CheckboxListTile(
title: Text('Modify advanced map settings'),
value: _advancedMapOptions ?? false,
onChanged: (newValue) {
Future.delayed(Duration.zero, () => {
if (newValue != _advancedMapOptions) setState(() {
_advancedMapOptions = newValue;
})
});
},
controlAffinity: ListTileControlAffinity.leading,
),
Visibility(
visible: _advancedMapOptions ?? false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Changing these settings can have serious performance impacts on your device and battery life',
style: TextStyle(
color: Colors.red[600],
fontSize: 12
),
textAlign: TextAlign.center,
),
),
//_buildInputTile(_mapQueryLimitController, 'Maximum other points', 'MAP.PROX.MAX_PER_QUERY', _advancedMapOptions, TextInputType.number, <TextInputFormatter>[ FilteringTextInputFormatter.allow('0-9') ], RegExp(r'\d+')),
//_buildInputTile(_mapQueryAgeController, 'Maximum age of other points (seconds)', 'MAP.PROX.MAX_AGE_SECONDS', _advancedMapOptions, TextInputType.number, <TextInputFormatter>[ FilteringTextInputFormatter.allow('0-9') ], RegExp(r'\d+')),
],
),
],
)
),
),
Spacer(flex: 1,),
// To force the row at the bottom of the screen
/// Action buttons (Confirm, Reset)
Row(
children: [
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () {
print(_formKey.currentState.validate());
if (_formKey.currentState.validate()) {
PreferencesHandler().setPreferencesBool('BT.USING_ADVANCED', _advancedBTOptions);
PreferencesHandler().setPreferencesString('BT.SERVICE_UUID', _bleServiceController.text);
PreferencesHandler().setPreferencesString('BT.CHARACTERISTIC_UUID', _bleCharacteristicController.text);
PreferencesHandler().setPreferencesBool('MAP.USING_ADVANCED', _advancedMapOptions);
}
},
child: Text('Confirm Changes'),
),
_buildInputTile(_mapQueryLimitController, 'Maximum other points', 'MAP.PROX.MAX_PER_QUERY', _advancedMapOptions, TextInputType.number, <TextInputFormatter>[ FilteringTextInputFormatter.allow('0-9') ], RegExp(r'\d+')),
_buildInputTile(_mapQueryAgeController, 'Maximum age of other points (seconds)', 'MAP.PROX.MAX_AGE_SECONDS', _advancedMapOptions, TextInputType.number, <TextInputFormatter>[ FilteringTextInputFormatter.allow('0-9') ], RegExp(r'\d+')),
],
),
],
)
),
),
// To force the row at the bottom of the screen
/// Action buttons (Confirm, Reset)
Row(
children: [
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () {
print(_formKey.currentState.validate());
if (_formKey.currentState.validate()) {
PreferencesHandler().setPreferencesBool('BT.USING_ADVANCED', _advancedBTOptions);
PreferencesHandler().setPreferencesString('BT.SERVICE_UUID', _bleServiceController.text);
PreferencesHandler().setPreferencesString('BT.CHARACTERISTIC_UUID', _bleCharacteristicController.text);
PreferencesHandler().setPreferencesBool('MAP.USING_ADVANCED', _advancedMapOptions);
}
},
child: Text('Confirm Changes'),
),
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () async {
PreferencesHandler().resetAllPreferences();
_formKey.currentState.reset();
},
child: Text('Reset values'),
),
),
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () async {
PreferencesHandler().resetAllPreferences();
_formKey.currentState.reset();
},
child: Text('Reset values'),
),
],
)
],
)
),
],
)
]
)
);
}
......@@ -202,36 +197,41 @@ class _SettingsFormState extends State<SettingsForm> {
/// TODO Individual field reset button key + '_DEFAULT'.
Widget _buildInputTile(TextEditingController controller, String labelText, String key, bool enabled, TextInputType inputType, List<TextInputFormatter> inputFormatters, RegExp validation) {
PreferencesHandler().getPreferences(key).then((value) => controller.text = value.toString());
return SizedBox(
height: 30,
return Container(
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 10),
child: Row(
children: [
TextFormField(
controller: controller,
enabled: enabled,
Expanded(
flex: 9,
child: TextFormField(
controller: controller,
enabled: enabled,
decoration: InputDecoration(
labelText: labelText,
contentPadding: EdgeInsets.zero,
),
keyboardType: inputType,
inputFormatters: inputFormatters,
validator: (value) {
if (value.isEmpty) {
return 'The field cannot be empty';
}
RegExpMatch match = validation.firstMatch(value);
if (match != null && match.input != value) {
return 'This is an invalid value';
}
decoration: InputDecoration(
labelText: labelText,
contentPadding: EdgeInsets.zero,
),
keyboardType: inputType,
inputFormatters: inputFormatters,
validator: (value) {
if (value.isEmpty) {
return 'The field cannot be empty';
return null;
}
RegExpMatch match = validation.firstMatch(value);
if (match != null && match.input != value) {
return 'This is an invalid value';
}
return null;
}
),
),
IconButton(
icon: Icon(Icons.restore),
onPressed: () => {}
Expanded(
flex: 1,
child: IconButton(
icon: Icon(Icons.restore),
onPressed: () => {}
)
)
],
)
......
......@@ -15,59 +15,43 @@ class DataWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget widget = Center(
child: SingleChildScrollView(
child: Column(
children: [
// Local data (Chart + Data points)
Graph(),
DataTile(DataHandler().getDataStream()),
// Bar (Locations + Add new -> Pop to new route)
Container(
color: Colors.grey,
child: Row(
children: [
Expanded(
child: Text(
'Other Locations',
textAlign: TextAlign.center,
),
child: Column(
children: [
// Local data (Chart + Data points)
Graph(),
DataTile(DataHandler().getDataStream()),
// Bar (Locations + Add new -> Pop to new route)
Container(
color: Colors.grey,
child: Row(
children: [
Expanded(
child: Text(
'Other Locations',
textAlign: TextAlign.center,
),
IconButton(
onPressed: () => {},
icon: Icon(Icons.add),
)
],
),
),
IconButton(
onPressed: () => {},
icon: Icon(Icons.add),
)
],
),
SingleChildScrollView(
child: Column(
children: [
],
),
),
SingleChildScrollView(
child: Column(
children: [
DataTile(DataHandler().getMockDataStream()),
DataTile(DataHandler().getMockDataStream()),
DataTile(DataHandler().getMockDataStream()),
DataTile(DataHandler().getMockDataStream()),
DataTile(DataHandler().getMockDataStream()),
DataTile(DataHandler().getMockDataStream()),
DataTile(DataHandler().getMockDataStream()),
],
),
// Locations
Container(
padding: EdgeInsets.symmetric(horizontal: 3),
height: 300,
child: Column(
children: [
Spacer(flex: 1),
StreamBuilder<Position>(
stream: PositionHandler().getCurrentOrLastPosition(),
builder: (context, snapshot) {
Position data = snapshot.data;
if (data != null)
return Text('Latitude ${data.latitude} | Longitude ${data.longitude}');
else return Text('Latitude N/A | Longitude N/A');
},
),
Spacer(flex: 1),
],
),
)
],
)
),
],
)
);
return CarouselCard(child: widget);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment