Merge branch 'develop' of github.com:invoiceninja/flutter-client into develop

This commit is contained in:
Hillel Coren 2020-11-08 04:59:35 +02:00
commit 27b71bdec3
9 changed files with 281 additions and 31 deletions

View File

@ -5,6 +5,9 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<application
android:label="Invoice Ninja"

View File

@ -82,6 +82,43 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.calendar
'PERMISSION_EVENTS=0',
## dart: PermissionGroup.reminders
'PERMISSION_REMINDERS=0',
## dart: PermissionGroup.contacts
# 'PERMISSION_CONTACTS=0',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=0',
## dart: PermissionGroup.microphone
'PERMISSION_MICROPHONE=0',
## dart: PermissionGroup.speech
'PERMISSION_SPEECH_RECOGNIZER=0',
## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=0',
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
'PERMISSION_LOCATION=0',
## dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=0',
## dart: PermissionGroup.mediaLibrary
'PERMISSION_MEDIA_LIBRARY=0',
## dart: PermissionGroup.sensors
'PERMISSION_SENSORS=0'
]
end
end
end

View File

@ -62,5 +62,7 @@
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
</dict>
</plist>

View File

@ -11,6 +11,8 @@ import 'package:invoiceninja_flutter/ui/client/edit/client_edit_vm.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:contacts_service/contacts_service.dart';
class ClientEditContacts extends StatefulWidget {
const ClientEditContacts({
@ -174,6 +176,7 @@ class ContactEditDetailsState extends State<ContactEditDetails> {
final _debouncer = Debouncer();
List<TextEditingController> _controllers = [];
Contact _contact;
void _onDoneContactPressed() {
if (widget.areButtonsVisible) {
@ -250,6 +253,27 @@ class ContactEditDetailsState extends State<ContactEditDetails> {
});
}
// Check contacts permission
Future<PermissionStatus> _getPermission() async {
final PermissionStatus permission = await Permission.contacts.status;
if (permission != PermissionStatus.granted &&
permission != PermissionStatus.denied) {
final Map<Permission, PermissionStatus> permissionStatus =
await [Permission.contacts].request();
return permissionStatus[Permission.contacts] ??
PermissionStatus.undetermined;
} else {
return permission;
}
}
void _setContactControllers(){
_firstNameController.text = _contact.givenName != null ? _contact.givenName: '';
_lastNameController.text = _contact.familyName != null ? _contact.familyName: '';
_emailController.text = _contact.emails.isNotEmpty ? _contact.emails.first.value : '';
_phoneController.text = _contact.phones.isNotEmpty ? _contact.phones.first.value : '';
}
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
@ -300,11 +324,35 @@ class ContactEditDetailsState extends State<ContactEditDetails> {
: Container(),
DecoratedFormField(
controller: _firstNameController,
label: localization.firstName,
validator: (String val) => !viewModel.client.hasNameSet
? AppLocalization.of(context).pleaseEnterAClientOrContactName
: null,
onSavePressed: (_) => _onDoneContactPressed(),
decoration: InputDecoration(
labelText: localization.firstName,
suffixIcon: IconButton(
alignment: Alignment.bottomCenter,
color: Theme.of(context).cardColor,
icon: Icon(
Icons.person,
color: Colors.grey,
),
onPressed: () async {
final PermissionStatus permissionStatus = await _getPermission();
if (permissionStatus == PermissionStatus.granted) {
try {
_contact = await ContactsService.openDeviceContactPicker();
setState(() {
_setContactControllers();
});
} catch (e) {
print(e.toString());
}
}
}
),
),
),
DecoratedFormField(
controller: _lastNameController,

View File

@ -11,6 +11,8 @@ import 'package:invoiceninja_flutter/ui/client/edit/client_edit_vm.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:contacts_service/contacts_service.dart';
class ClientEditDetails extends StatefulWidget {
const ClientEditDetails({
@ -37,6 +39,7 @@ class ClientEditDetailsState extends State<ClientEditDetails> {
final _debouncer = Debouncer();
List<TextEditingController> _controllers;
Contact _contact;
@override
void didChangeDependencies() {
@ -101,6 +104,25 @@ class ClientEditDetailsState extends State<ClientEditDetails> {
});
}
//Check contacts permission
Future<PermissionStatus> _getPermission() async {
final PermissionStatus permission = await Permission.contacts.status;
if (permission != PermissionStatus.granted &&
permission != PermissionStatus.denied) {
final Map<Permission, PermissionStatus> permissionStatus =
await [Permission.contacts].request();
return permissionStatus[Permission.contacts] ??
PermissionStatus.undetermined;
} else {
return permission;
}
}
void _setContactControllers(){
_nameController.text = _contact.displayName;
_phoneController.text = _contact.phones.first.value;
}
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
@ -114,13 +136,36 @@ class ClientEditDetailsState extends State<ClientEditDetails> {
FormCard(
children: <Widget>[
DecoratedFormField(
autofocus: true,
label: localization.name,
autofocus: true,
controller: _nameController,
validator: (String val) => !viewModel.client.hasNameSet
? AppLocalization.of(context).pleaseEnterAClientOrContactName
: null,
onSavePressed: viewModel.onSavePressed,
decoration: InputDecoration(
labelText: localization.name,
suffixIcon: IconButton(
alignment: Alignment.bottomCenter,
color: Theme.of(context).cardColor,
icon: Icon(
Icons.person,
color: Colors.grey,
),
onPressed: () async {
final PermissionStatus permissionStatus = await _getPermission();
if (permissionStatus == PermissionStatus.granted) {
try {
_contact = await ContactsService.openDeviceContactPicker();
setState(() {
_setContactControllers();
});
} catch (e) {
print(e.toString());
}
}
}
),
),
),
DynamicSelector(
entityType: EntityType.group,

View File

@ -8,6 +8,8 @@ import 'package:invoiceninja_flutter/ui/vendor/edit/vendor_edit_contacts_vm.dart
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:contacts_service/contacts_service.dart';
class VendorEditContacts extends StatefulWidget {
const VendorEditContacts({
@ -159,6 +161,7 @@ class VendorContactEditDetailsState extends State<VendorContactEditDetails> {
final _debouncer = Debouncer();
List<TextEditingController> _controllers = [];
Contact _contact;
@override
void didChangeDependencies() {
@ -211,6 +214,27 @@ class VendorContactEditDetailsState extends State<VendorContactEditDetails> {
});
}
void _setContactControllers(){
_firstNameController.text = _contact.givenName != null ? _contact.givenName: '';
_lastNameController.text = _contact.familyName != null ? _contact.familyName: '';
_emailController.text = _contact.emails.isNotEmpty ? _contact.emails.first.value : '';
_phoneController.text = _contact.phones.isNotEmpty ? _contact.phones.first.value : '';
}
// Check contacts permission
Future<PermissionStatus> _getPermission() async {
final PermissionStatus permission = await Permission.contacts.status;
if (permission != PermissionStatus.granted &&
permission != PermissionStatus.denied) {
final Map<Permission, PermissionStatus> permissionStatus =
await [Permission.contacts].request();
return permissionStatus[Permission.contacts] ??
PermissionStatus.undetermined;
} else {
return permission;
}
}
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
@ -261,7 +285,30 @@ class VendorContactEditDetailsState extends State<VendorContactEditDetails> {
: Container(),
DecoratedFormField(
controller: _firstNameController,
label: localization.firstName,
decoration: InputDecoration(
labelText: localization.firstName,
suffixIcon: IconButton(
alignment: Alignment.bottomCenter,
color: Theme.of(context).cardColor,
icon: Icon(
Icons.person,
color: Colors.grey,
),
onPressed: () async {
final PermissionStatus permissionStatus = await _getPermission();
if (permissionStatus == PermissionStatus.granted) {
try {
_contact = await ContactsService.openDeviceContactPicker();
setState(() {
_setContactControllers();
});
} catch (e) {
print(e.toString());
}
}
}
),
),
),
DecoratedFormField(
controller: _lastNameController,

View File

@ -8,6 +8,8 @@ import 'package:invoiceninja_flutter/ui/app/forms/user_picker.dart';
import 'package:invoiceninja_flutter/ui/vendor/edit/vendor_edit_vm.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:contacts_service/contacts_service.dart';
class VendorEditDetails extends StatefulWidget {
const VendorEditDetails({
@ -34,6 +36,7 @@ class VendorEditDetailsState extends State<VendorEditDetails> {
final _debouncer = Debouncer();
List<TextEditingController> _controllers;
Contact _contact;
@override
void didChangeDependencies() {
@ -98,6 +101,25 @@ class VendorEditDetailsState extends State<VendorEditDetails> {
});
}
void _setContactControllers(){
_nameController.text = _contact.displayName != null ? _contact.displayName : '';
_phoneController.text = _contact.phones.isNotEmpty ? _contact.phones.first.value : '';
}
// Check contacts permission
Future<PermissionStatus> _getPermission() async {
final PermissionStatus permission = await Permission.contacts.status;
if (permission != PermissionStatus.granted &&
permission != PermissionStatus.denied) {
final Map<Permission, PermissionStatus> permissionStatus =
await [Permission.contacts].request();
return permissionStatus[Permission.contacts] ??
PermissionStatus.undetermined;
} else {
return permission;
}
}
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
@ -112,10 +134,33 @@ class VendorEditDetailsState extends State<VendorEditDetails> {
DecoratedFormField(
autofocus: true,
controller: _nameController,
label: localization.name,
validator: (String val) => val == null || val.isEmpty
? AppLocalization.of(context).pleaseEnterAName
: null,
decoration: InputDecoration(
labelText: localization.firstName,
suffixIcon: IconButton(
alignment: Alignment.bottomCenter,
color: Theme.of(context).cardColor,
icon: Icon(
Icons.person,
color: Colors.grey,
),
onPressed: () async {
final PermissionStatus permissionStatus = await _getPermission();
if (permissionStatus == PermissionStatus.granted) {
try {
_contact = await ContactsService.openDeviceContactPicker();
setState(() {
_setContactControllers();
});
} catch (e) {
print(e.toString());
}
}
}
),
),
),
UserPicker(
userId: vendor.assignedUserId,

View File

@ -35,14 +35,14 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety"
version: "2.5.0-nullsafety.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety"
version: "2.1.0-nullsafety.1"
build:
dependency: transitive
description:
@ -119,14 +119,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.2"
version: "1.1.0-nullsafety.3"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety"
version: "1.2.0-nullsafety.1"
charts_common:
dependency: transitive
description:
@ -161,7 +161,7 @@ packages:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety"
version: "1.1.0-nullsafety.1"
code_builder:
dependency: transitive
description:
@ -175,7 +175,14 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0-nullsafety.2"
version: "1.15.0-nullsafety.3"
contacts_service:
dependency: "direct main"
description:
name: contacts_service
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.6"
convert:
dependency: transitive
description:
@ -252,7 +259,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety"
version: "1.2.0-nullsafety.1"
faker:
dependency: "direct dev"
description:
@ -273,7 +280,7 @@ packages:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0-nullsafety.1"
version: "6.0.0-nullsafety.2"
firebase:
dependency: transitive
description:
@ -599,7 +606,7 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety"
version: "0.12.10-nullsafety.1"
material_design_icons_flutter:
dependency: "direct main"
description:
@ -620,7 +627,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.2"
version: "1.3.0-nullsafety.4"
mime:
dependency: transitive
description:
@ -697,7 +704,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety"
version: "1.8.0-nullsafety.1"
path_provider:
dependency: "direct main"
description:
@ -739,7 +746,21 @@ packages:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.2"
version: "1.10.0-nullsafety"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.1+1"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
petitparser:
dependency: transitive
description:
@ -753,7 +774,7 @@ packages:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0-nullsafety.1"
version: "3.0.0-nullsafety.2"
plugin_platform_interface:
dependency: transitive
description:
@ -774,7 +795,7 @@ packages:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-nullsafety.1"
version: "4.0.0-nullsafety.2"
pub_semver:
dependency: transitive
description:
@ -940,7 +961,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety"
version: "1.8.0-nullsafety.2"
sqflite:
dependency: transitive
description:
@ -961,14 +982,14 @@ packages:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety"
version: "1.10.0-nullsafety.2"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety"
version: "2.1.0-nullsafety.1"
stream_transform:
dependency: transitive
description:
@ -982,7 +1003,7 @@ packages:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety"
version: "1.1.0-nullsafety.1"
sync_http:
dependency: transitive
description:
@ -1003,28 +1024,28 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety"
version: "1.2.0-nullsafety.1"
test:
dependency: "direct dev"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0-nullsafety.4"
version: "1.16.0-nullsafety.5"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19-nullsafety"
version: "0.2.19-nullsafety.2"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.12-nullsafety.4"
version: "0.3.12-nullsafety.5"
timeago:
dependency: "direct main"
description:
@ -1045,7 +1066,7 @@ packages:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.2"
version: "1.3.0-nullsafety.3"
url_launcher:
dependency: "direct main"
description:
@ -1115,7 +1136,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.2"
version: "2.1.0-nullsafety.3"
version:
dependency: "direct main"
description:

View File

@ -52,12 +52,14 @@ dependencies:
package_info: ^0.4.0+16
device_info: ^0.4.2+1
rounded_loading_button: ^1.0.0
#quick_actions: ^0.2.1
# quick_actions: ^0.2.1
version: ^1.0.0
#idb_shim: ^1.11.1+1
# idb_shim: ^1.11.1+1
flutter_launcher_icons: ^0.8.0
overflow_view: ^0.2.1
flutter_styled_toast: ^1.4.0+1
permission_handler: ^5.0.1+1
contacts_service: ^0.4.6
dev_dependencies:
flutter_driver: