From 080c7ec471e49e417a0d542ec41320857c01f2a1 Mon Sep 17 00:00:00 2001 From: Ivan Murashov Date: Mon, 24 Jul 2017 18:07:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D0=BA=D1=83=D0=BF=D0=BA=D0=B8=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D1=81=D1=83=D0=BC=D0=BC=D0=B0=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BA=D1=83=D0=BF=D0=BA=D0=B8,=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=BD=D0=B0=20=D1=8D?= =?UTF-8?q?=D0=BA=D1=80=D0=B0=D0=BD=20=D0=BF=D0=BE=D0=B4=D1=82=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 2 + .../dinect/checker/activity/MainActivity.java | 16 ++-- lib/activate_token.dart | 64 +++++++++------- lib/base_state.dart | 48 +++++++++++- lib/main.dart | 31 ++++---- lib/purchase.dart | 75 ++++++++++--------- lib/purchase_success.dart | 31 +++++--- lib/registration.dart | 71 +++++++----------- lib/splash.dart | 2 +- 9 files changed, 200 insertions(+), 140 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d7a6f2b..4935ac8 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ { + bool _tokenActive = true; + String _merchantID = ''; + + _RegistrationScreenState() { + if (textFieldValue == "") { + _getSavedMerchantID(); + } + } + @override String getTitle() { return "Регистрация"; } @override getHint() { - return 'ID merchant'; + return 'ID магазина'; } @overide getMenuButtons(BuildContext context) { return [new IconButton(icon: new Icon(Icons.help_outline), onPressed: () {})]; } - @override Widget _getScreenContent(BuildContext context) { - if (textFieldValue == "") { - _getSavedValue(); - } + @override Widget getScreenContent() { return new Column(children: [ getLogo(), getHintLabel(), getDecoratedTextWidget(), _getMessage(), - _getButton(context) + buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), + buildRaisedButton(context, _tokenActive ? 'ЗАВЕРШИТЬ РЕГИСТРАЦИЮ' : 'ОБНОВИТЬ СТАТУС АКТИВАЦИИ',() => startScanner(context))) ]); } - _getMerchantID() { - return new Text(_merchantID != null ? _merchantID : '', style: new TextStyle(color: Colors.black, fontSize: 16.0)); + @override Widget getTextWidget() { + return new Row(children: [new Text(_merchantID != null ? _merchantID : '', style: new TextStyle(color: Colors.black, fontSize: 16.0))]); } _getSavedMerchantID() { - const platform = const MethodChannel('com.dinect.checker/instance_id'); platform.invokeMethod('getMerchantID').then((result) { setState(() { _merchantID = result; - print(_merchantID); + print('merchanID: ${_merchantID}'); }); }); } @@ -53,7 +59,7 @@ class _RegistrationScreenState extends BaseState { _getMessage() { return new Container(height: _tokenActive ? 72.0 : 108.0, decoration: _getDecoraionForMessageField(), margin: new EdgeInsets.only(top: 20.0, left: 12.0, right: 12.0), - padding: new EdgeInsets.only(bottom: 16.0, left: 14.0, right: 14.0), + padding: new EdgeInsets.only(bottom: 22.0, left: 14.0, right: 14.0), child: new Center(child: new Text(_getMessageText(), textAlign: TextAlign.center, style: new TextStyle(height: 1.5, fontWeight: FontWeight.bold, fontSize: 14.0, color: _tokenActive ? tokenActiveTextColor : tokenActivateTextColor)))); } @@ -72,28 +78,32 @@ class _RegistrationScreenState extends BaseState { double buttonHeight = 42.0; double topMargin = 8.0; return new Container(margin: new EdgeInsets.only(top: topMargin), height: buttonHeight, - child: new RaisedButton(child: new Text(_tokenActive ? 'ЗАВЕРШИТЬ РЕГИСТРАЦИЮ' : 'ОБНОВИТЬ СТАТУС АКТИВАЦИИ', + child: new RaisedButton(child: new Text(, style: new TextStyle(fontSize: 14.0, color: Colors.white)), onPressed: () { - if (_tokenActive) { + startScanner(context); - } else { - checkToken(context).then((response) { - print(response.body); - Map parsedMap = JSON.decode(response.body); - // Обновить экран, заменить сообщение о необходимости активации токена, на сообщние о том, что токен активен. - setState(() { - _tokenActive = parsedMap['active']; - }); + // if (_tokenActive) { + // startScanner(context); + // } else { + // checkToken(context).then((response) { - }).catchError((error) { - print(error.toString()); - return false; - }); - } + // print(response.body); + // Map parsedMap = JSON.decode(response.body); + + // // Обновить экран, заменить сообщение о необходимости активации токена, на сообщние о том, что токен активен. + // setState(() { + // _tokenActive = parsedMap['active']; + // }); + + // }).catchError((error) { + // print(error.toString()); + // return false; + // }); + // } }, color: primaryColor)); } -} +} \ No newline at end of file diff --git a/lib/base_state.dart b/lib/base_state.dart index 1945952..09fe37d 100644 --- a/lib/base_state.dart +++ b/lib/base_state.dart @@ -11,6 +11,8 @@ abstract class BaseState extends State { String error = null; String textFieldValue = ""; + TextEditingController controller = new TextEditingController(); + @override Widget build(BuildContext context) { return new Scaffold(appBar: getAppBar(context), body: getBody(context)); } @@ -57,6 +59,13 @@ abstract class BaseState extends State { } } + /// Смена состояния экрана при изменении текста в поле ввода. + handleUserInput(String text) { + setState(() { + textFieldValue = text; + }); + } + /// Метод возвращает контейнер с установленными отступами, в котором размещен TextField обернутый в BoxDecoration. getDecoratedTextWidget() { return new Container(margin: new EdgeInsets.only(left: verticalMargin, right: verticalMargin), @@ -76,16 +85,21 @@ abstract class BaseState extends State { /// Метод возвращает BoxDecoration для _getDecoratedInputField getDecoraionForTextWidget() { - return new BoxDecoration(color: textFieldBackground, + return new BoxDecoration(color: getTextFilledBackground(), border: new Border.all(color: textBorderColor, width: 1.0,), borderRadius: new BorderRadius.all(new Radius.circular(4.0))); } + Color getTextFilledBackground() { + return const Color(0xffefefef); + } + Widget getTextWidget() { return new TextField(keyboardType: TextInputType.number, decoration: new InputDecoration.collapsed(hintText: getHint(), hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)), - onChanged: (text) => _handleUserInput(text)); + controller: controller, + onChanged: (text) => handleUserInput(text)); } /// Индикация ... @@ -93,5 +107,35 @@ abstract class BaseState extends State { return new Center(child: loading ? new CircularProgressIndicator() : null); } + Widget getValueWithTitle(String title, String value) { + return new Container(padding: new EdgeInsets.only(left: verticalMargin, right: verticalMargin, top: 18.0), + child: new Column(children: [ + new Row(crossAxisAlignment: CrossAxisAlignment.start, children: [new Text(title, textAlign: TextAlign.left, style: new TextStyle(color: greyTextColor, fontSize: 14.0))]), + new Row(crossAxisAlignment: CrossAxisAlignment.start, children: [new Text(value, textAlign: TextAlign.left, style: new TextStyle(color: Colors.black, fontSize: 20.0))]) + ])); + } + Widget buildButton(EdgeInsets margin, Widget widget) { + return new Container(margin: margin, height: buttonHeight, child: new Row(children: [new Expanded(child: widget)])); + } + + Widget buildRaisedButton(BuildContext context, String text, VoidCallback onPressed) { + return new RaisedButton(child: new Text(text, + style: new TextStyle(color: Colors.white)), + onPressed: onPressed, + color: primaryColor); + } + + Widget buildFlatButton(BuildContext context, String title, Color textColor) { + return new Container(height: buttonHeight, child: new FlatButton(child: new Text(title, + style: new TextStyle(color: textColor)), + onPressed: () => startScanner(context)), + decoration: _getDecoraionForScanButton()); + } + + _getDecoraionForScanButton() { + return new BoxDecoration( + border: new Border.all(color: primaryColor, width: 1.0), + borderRadius: new BorderRadius.all(new Radius.circular(4.0))); + } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index cacab5c..0115fc9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -33,8 +33,13 @@ const Color tokenActiveTextColor = const Color(0xff1f5a1f); const Color tokenActivateTextColor = const Color(0xff4e3a19); const Color greenBackground = const Color(0xff8ae28a); -// Margins +// Dimens const double verticalMargin = 28.0; +const double buttonVerticalMargin = 64.0; +const double buttonHeight = 48.0; +const double iconHeight = 20.0; + +const platform = const MethodChannel('com.dinect.checker/instance_id'); // HttpClient final httpClient = createHttpClient(); @@ -56,14 +61,12 @@ checkToken(BuildContext context) async { /// Может производиться с нескольких экранов (splash, finish_registration). startScanner(BuildContext context) async { - const platform = const MethodChannel('com.dinect.checker/instance_id'); - // Канал слушает ловит вызовы методов из "нативной" части приложения. // Могут быть вызваны либо logaut либо faq, либо purchase. platform.setMethodCallHandler((MethodCall call) async { if (call.method == 'foo') { - logout(); + logout(context); } else { pushRoute(context, new PurchaseScreen()); } @@ -71,22 +74,24 @@ startScanner(BuildContext context) async { return result; }); - + Navigator.of(context).pop(); await platform.invokeMethod('startScanner'); } logout(BuildContext context) { - // String url = intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken; + String url = intUrl + 'tokens/' + 'khooi' + '?_dmapptoken=' + intToken; + print(url); + httpClient.delete(url).then((response) { + print(response.body); + const platform = const MethodChannel('com.dinect.checker/instance_id'); + platform.invokeMethod('removeKeys'); + pushRoute(context, new RegistrationScreen()); + }).catchError((error) { + print(error.toString()); + }); - // httpClient.delete(url).then((response) { - // print(response.body); - // }).catchError((error) { - // print(error.toString()); - // }); - - pushRoute(context, new RegistrationScreen()); } /// Навигация по приложению. diff --git a/lib/purchase.dart b/lib/purchase.dart index ed164de..332ed01 100644 --- a/lib/purchase.dart +++ b/lib/purchase.dart @@ -14,7 +14,7 @@ class PurchaseScreen extends StatefulWidget { class PurchaseScreenState extends BaseState { - String _sum = "1234.00"; + String integerPart = '', fractionalPart = ''; @override String getTitle() { return "Проведение покупки"; @@ -27,7 +27,7 @@ class PurchaseScreenState extends BaseState { @overide getMenuButtons(BuildContext context) { return [ new IconButton(icon: new Icon(Icons.help_outline), onPressed: () {}), - new IconButton(icon: new Image.asset(logout_png, height: 20.0, width: 20.0), onPressed: () => logout(context)) + new IconButton(icon: new Image.asset(logout_png, height: iconHeight, width: iconHeight), onPressed: () => logout(context)) ]; } @@ -40,47 +40,53 @@ class PurchaseScreenState extends BaseState { getValueWithTitle('Вознаграждение', '100%'), getHintLabel(), getDecoratedTextWidget(), - buildButton(new EdgeInsets.only(top: 36.0, left: 70.0, right: 70.0), buildRaisedButton(context, 'ЗАВЕРШИТЬ ПОКУПКУ', () => _purchase(context))), - buildButton(new EdgeInsets.only(top: 24.0, left: 70.0, right: 70.0), buildFlatButton(context, 'СКАНИРОВАТЬ', primaryColor))]) + buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildRaisedButton(context, 'ЗАВЕРШИТЬ ПОКУПКУ', () => _purchase(context))), + buildButton(new EdgeInsets.only(top: 24.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildFlatButton(context, 'СКАНИРОВАТЬ', primaryColor))]) ].reversed.toList())); } - Widget getValueWithTitle(String title, String value) { - return new Container(padding: new EdgeInsets.only(left: verticalMargin, right: verticalMargin, top: 18.0), - child: new Column(children: [ - new Row(crossAxisAlignment: CrossAxisAlignment.start, children: [new Text(title, textAlign: TextAlign.left, style: new TextStyle(color: greyTextColor, fontSize: 14.0))]), - new Row(crossAxisAlignment: CrossAxisAlignment.start, children: [new Text(value, textAlign: TextAlign.left, style: new TextStyle(color: Colors.black, fontSize: 20.0))]) - ])); + @override Color getTextFilledBackground() { + return Colors.white; } - Widget buildButton(EdgeInsets margin, Widget widget) { - return new Container(margin: margin, height: 48.0, child: new Row(children: [new Expanded(child: widget)])); - } - - Widget buildRaisedButton(BuildContext context, String text, VoidCallback onPressed) { - return new RaisedButton(child: new Text(text, - style: new TextStyle(color: Colors.white)), - onPressed: onPressed, - color: primaryColor); + /// Смена состояния экрана при изменении текста в поле ввода. + @override handleUserInput(String tmpString) { + setState(() { + tmpString = tmpString.replaceAll('-', ''); + tmpString = tmpString.replaceAll(', ', ''); + print(tmpString); + if (tmpString.contains('.')) { + int dotIndex = tmpString.indexOf('.'); + integerPart = tmpString.substring(0, dotIndex); + fractionalPart = tmpString.substring(dotIndex + 1, tmpString.length); + if (fractionalPart.length > 2) { + fractionalPart = fractionalPart.substring(0, 2); + } + controller.text = '${integerPart}.${fractionalPart}'; + } else { + integerPart = tmpString; + controller.text = tmpString; + } + textFieldValue = controller.text; + }); } - Widget buildFlatButton(BuildContext context, String title, Color textColor) { - return new Container(height: 48.0, child: new FlatButton(child: new Text(title, - style: new TextStyle(color: textColor)), - onPressed: () => startScanner(context)), - decoration: _getDecoraionForScanButton()); - } + _buildSum() { + String temporaryInteger = integerPart; + String temporaryFractional = fractionalPart; - _getDecoraionForScanButton() { - return new BoxDecoration( - border: new Border.all(color: primaryColor, width: 1.0), - borderRadius: new BorderRadius.all(new Radius.circular(4.0))); + while (temporaryFractional.length < 2) { + temporaryFractional = temporaryFractional + '0'; + } + + return temporaryInteger + '.' + temporaryFractional; } _purchase(BuildContext context) { + String val = _buildSum(); showDialog(context: context, child: new AlertDialog( title: new Text('Подтверждение'), - content: new Text('Вы подтверждаете покупку на ${_sum} руб?'), + content: new Text('Вы подтверждаете покупку на ${val} руб?'), actions: [ new FlatButton( child: new Text('Нет'), @@ -89,16 +95,11 @@ class PurchaseScreenState extends BaseState { }, ), new FlatButton( - child: new Text('Дат'), + child: new Text('Да'), onPressed: () { - pushRoute(context, new PurchaseSuccessScreen()); + pushRoute(context, new PurchaseSuccessScreen(val)); }, ) ])); - print('purchase'); - } - - _register(BuildContext context) async { - } } \ No newline at end of file diff --git a/lib/purchase_success.dart b/lib/purchase_success.dart index 7efc8c4..cb433c6 100644 --- a/lib/purchase_success.dart +++ b/lib/purchase_success.dart @@ -7,10 +7,23 @@ import 'purchase.dart'; /// Экран проведения покупки. class PurchaseSuccessScreen extends StatefulWidget { - @override State createState() => new _PurchaseSuccessScreenState(); + + String val = ''; + + PurchaseSuccessScreen(String val) { + this.val = val; + } + + @override State createState() => new PurchaseSuccessScreenState(val); } -class _PurchaseSuccessScreenState extends PurchaseScreenState { +class PurchaseSuccessScreenState extends PurchaseScreenState { + + String val = ''; + + PurchaseSuccessScreenState(String val) { + this.val = val; + } @override String getTitle() { return "Проведение покупки"; @@ -25,17 +38,17 @@ class _PurchaseSuccessScreenState extends PurchaseScreenState { @override Widget getScreenContent() { return new Column(children: [ - getValueWithTitle('Покупатель', 'Знаменитый Рокер Паук'), - _getSuccessMessage(), - new Expanded(child: new Center()), - buildButton(new EdgeInsets.only(bottom: 74.0, left: 70.0, right: 70.0), buildRaisedButton(context, 'СКАНИРОВАТЬ', () => startScanner(context))) - ]); + getValueWithTitle('Покупатель', 'Знаменитый Рокер Паук'), + getSuccessMessage(), + new Expanded(child: new Center()), + buildButton(new EdgeInsets.only(bottom: 74.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildRaisedButton(context, 'СКАНИРОВАТЬ', () => startScanner(context))) + ]); } - _getSuccessMessage() { + getSuccessMessage() { return new Row(children: [new Expanded(child: new Container(margin: new EdgeInsets.only(top: 20.0), height: 64.0, decoration: new BoxDecoration(color: greenBackground), - child: new Center(child: new Text('Покупка на сумму 1234.00 руб. проведена', textAlign: TextAlign.center, + child: new Center(child: new Text('Покупка на сумму ${val} руб. проведена', textAlign: TextAlign.center, style: new TextStyle(fontWeight: FontWeight.bold, color: tokenActiveTextColor)))))]); } diff --git a/lib/registration.dart b/lib/registration.dart index c3dde74..2df96ad 100644 --- a/lib/registration.dart +++ b/lib/registration.dart @@ -21,7 +21,7 @@ class _RegistrationScreenState extends BaseState { } @override getHint() { - return 'ID merchant'; + return 'ID магазина'; } @overide getMenuButtons(BuildContext context) { @@ -37,35 +37,17 @@ class _RegistrationScreenState extends BaseState { getLogo(), getHintLabel(), getDecoratedTextWidget(), - _getButton(context)])) + buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), + buildRaisedButton(context, 'ЗАРЕГИСТРИРОВАТЬ', _isValidMerchantID() && !loading ? () => _registerShop(context) : null))])) ].reversed.toList())); } - /// Метод возвращает кнопку, которая запускает отправку токена кассы на сервер. - _getButton(BuildContext context) { - double buttonHeight = 42.0; - double topMargin = 36.0; - double horizontalPadding = 40.0; // Отступы по краям от кнопки. - return new Container(margin: new EdgeInsets.only(top: topMargin), height: buttonHeight, - padding: new EdgeInsets.only(left: horizontalPadding, right: horizontalPadding), - child: new RaisedButton(child: new Text('ЗАРЕГИСТРИРОВАТЬ', - style: new TextStyle(color: Colors.white)), - onPressed: _isValidMerchantID() && !loading ? () => _registerShop(context) : null, - color: primaryColor)); - } - /// Токен кассы - это DIN код. DIN код - это специальный код динекта, максимальная его длина - 25 символов. _isValidMerchantID() { + print("${textFieldValue.length}"); return textFieldValue.length > 0 && textFieldValue.length < 25; } - /// Смена состояния экрана при изменении текста в поле ввода. - _handleUserInput(String text) { - setState(() { - textFieldValue = text; - }); - } - /// Показать индикатор, запросить токен. _registerShop(BuildContext context) { setState(() { @@ -84,7 +66,6 @@ class _RegistrationScreenState extends BaseState { /// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена. _register(BuildContext context) async { - const platform = const MethodChannel('com.dinect.checker/instance_id'); String url = intUrl + 'tokens/?_dmapptoken=' + intToken; String pos = (new DateTime.now().millisecondsSinceEpoch / 1000).toString(); @@ -95,31 +76,33 @@ class _RegistrationScreenState extends BaseState { 'pos': pos, }; - httpClient.post(url, body: body).then((response) { + pushRoute(context, new FinishRegistrationScreen()); + + // httpClient.post(url, body: body).then((response) { setState(() { error = null; }); - print(response.body); - Map parsedMap = JSON.decode(response.body); - setState(() { - loading = false; - }); - if (response.statusCode == 201) { - token = parsedMap['token']; - platform.invokeMethod('saveToken', {'token' : token}); - platform.invokeMethod('saveMerchantID', {'merchantID' : merchantID}); - pushRoute(context, new FinishRegistrationScreen()); - } else { - setState(() { - error = parsedMap['errors'][0]; - }); - } - }).catchError((error) { - setState(() { - error = 'Отсутствует интернет соединение'; - }); - }); + // print(response.body); + // Map parsedMap = JSON.decode(response.body); + // setState(() { + // loading = false; + // }); + // if (response.statusCode == 201) { + // token = parsedMap['token']; + // platform.invokeMethod('saveToken', {'token' : token}); + // platform.invokeMethod('saveMerchantID', {'merchantID' : merchantID}); + // pushRoute(context, new FinishRegistrationScreen()); + // } else { + // setState(() { + // error = parsedMap['errors'][0]; + // }); + // } + // }).catchError((error) { + // setState(() { + // error = 'Отсутствует интернет соединение'; + // }); + // }); } } \ No newline at end of file diff --git a/lib/splash.dart b/lib/splash.dart index 8ac45d0..3bb6419 100644 --- a/lib/splash.dart +++ b/lib/splash.dart @@ -30,6 +30,7 @@ class SplashScreen extends StatelessWidget { const platform = const MethodChannel('com.dinect.checker/instance_id'); token = await platform.invokeMethod('getToken'); print('token: $token'); + // В случае, если в приложении отсутствует токен, // необходимо запустить регистрацию кассы. @@ -44,7 +45,6 @@ class SplashScreen extends StatelessWidget { bool active = parsedMap['active']; if (active) { - Navigator.of(context).pop(); // Запускается экран сканера, токен кассы активирован, с его помощью можно делать запросы к pos-api. startScanner(context); } else {