diff --git a/android/app/src/main/java/com/dinect/checker/activity/CameraActivity.java b/android/app/src/main/java/com/dinect/checker/activity/CameraActivity.java index a39621c..12215a3 100644 --- a/android/app/src/main/java/com/dinect/checker/activity/CameraActivity.java +++ b/android/app/src/main/java/com/dinect/checker/activity/CameraActivity.java @@ -2,6 +2,7 @@ package com.dinect.checker.activity; import android.support.v7.app.AppCompatActivity; import android.content.pm.ActivityInfo; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.Log; @@ -69,7 +70,7 @@ public class CameraActivity extends AppCompatActivity { if (actionBar != null) { actionBar.setTitle(getString(R.string.scanner_title)); - actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); } mCamera = getCameraInstance(); @@ -117,6 +118,12 @@ public class CameraActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } + @Override + public void onBackPressed() { + setResult(RESULT_CANCELED); + finish(); + } + @Override public void onDestroy() { super.onDestroy(); @@ -193,6 +200,10 @@ public class CameraActivity extends AppCompatActivity { SymbolSet syms = mScanner.getResults(); for (Symbol sym : syms) { mBarcodeScanned = true; + Intent intent = new Intent(); + intent.putExtra("code", sym.getData()); + setResult(RESULT_OK, intent); + finish(); Toast.makeText(CameraActivity.this, sym.getData(), Toast.LENGTH_SHORT).show(); } } diff --git a/android/app/src/main/java/com/dinect/checker/activity/MainActivity.java b/android/app/src/main/java/com/dinect/checker/activity/MainActivity.java index 04c410e..aee24b7 100644 --- a/android/app/src/main/java/com/dinect/checker/activity/MainActivity.java +++ b/android/app/src/main/java/com/dinect/checker/activity/MainActivity.java @@ -19,8 +19,11 @@ import java.util.Map; public class MainActivity extends FlutterActivity { private static final int START_SCANNER_REQUEST_CODE = 2017; + private static final String PREF_POS_TOKEN = "pref_pos_token"; + private static final String PREF_POS_MERCHANT_ID = "pref_pos_merchant_id"; private MethodChannel mChannel; + private SharedPreferences mPreferences; @Override protected void onCreate(Bundle savedInstanceState) { @@ -28,8 +31,7 @@ public class MainActivity extends FlutterActivity { GeneratedPluginRegistrant.registerWith(this); final String INSTANCE_ID_CHANNEL = "com.dinect.checker/instance_id"; - final String PREF_POS_TOKEN = "pref_pos_token"; - final SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE); + mPreferences = getPreferences(Context.MODE_PRIVATE); mChannel = new MethodChannel(getFlutterView(), INSTANCE_ID_CHANNEL); mChannel.setMethodCallHandler( @@ -47,13 +49,18 @@ public class MainActivity extends FlutterActivity { } break; case "saveToken": - Map arguments = call.arguments(); - String token = (String) arguments.get("token"); - Log.d("kifio", token); - preferences.edit().putString(PREF_POS_TOKEN, token).apply(); + Map tokenArguments = call.arguments(); + mPreferences.edit().putString(PREF_POS_TOKEN, (String) tokenArguments.get("token")).apply(); break; case "getToken": - result.success(preferences.getString(PREF_POS_TOKEN, null)); + result.success(mPreferences.getString(PREF_POS_TOKEN, null)); + break; + case "saveMerchantID": + Map merchantIDArguments = call.arguments(); + mPreferences.edit().putString(PREF_POS_MERCHANT_ID, (String) merchantIDArguments.get("merchantID")).apply(); + break; + case "getMerchantID": + result.success(mPreferences.getString(PREF_POS_MERCHANT_ID, null)); break; case "startScanner": Intent cameraIntent = new Intent(MainActivity.this, CameraActivity.class); @@ -69,11 +76,28 @@ public class MainActivity extends FlutterActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == START_SCANNER_REQUEST_CODE && resultCode == RESULT_OK) { - mChannel.invokeMethod("foo", null); + if (requestCode == START_SCANNER_REQUEST_CODE && resultCode == RESULT_CANCELED) { + finish(); + } else if (requestCode == START_SCANNER_REQUEST_CODE && resultCode == RESULT_OK) { + if (data == null) { + logout(); + } else { + String code = data.getExtras().getString("code", null); + if (code == null) { + logout(); + } else { + mChannel.invokeMethod("purchase", code); + } + } } } + private void logout() { + mChannel.invokeMethod("foo", null); + mPreferences.edit().remove(PREF_POS_TOKEN).apply(); + mPreferences.edit().remove(PREF_POS_MERCHANT_ID).apply(); + } + public void startScanner() { } @@ -90,4 +114,12 @@ public class MainActivity extends FlutterActivity { } + public void saveMerchantID() { + + } + + public void getMerchantID() { + + } + } diff --git a/android/app/src/main/res/layout/a_scanner.xml b/android/app/src/main/res/layout/a_scanner.xml index c3a0be0..4fd7ecc 100644 --- a/android/app/src/main/res/layout/a_scanner.xml +++ b/android/app/src/main/res/layout/a_scanner.xml @@ -10,6 +10,39 @@ android:layout_width="match_parent" android:layout_height="match_parent"/> + + + + + + + + + + + + + + + + new _RegistrationScreenState(); } class _RegistrationScreenState extends State { + bool _tokenActive = false; + String _merchantID = null; + @override Widget build(BuildContext context) { return new Scaffold(appBar: _getAppBar(), body: _getScreen(context)); } AppBar _getAppBar() { - return new AppBar(title: new Text("Регистрация магазина"), + return new AppBar(title: new Text("Регистрация", style: new TextStyle(fontSize: 18.0)), backgroundColor: primaryColor, actions: [ new IconButton( icon: new Icon(Icons.help_outline), @@ -23,67 +28,97 @@ class _RegistrationScreenState extends State { } Widget _getScreen(BuildContext context) { - return new Center(child: new Column(children: [ + if (_merchantID == null) { + _getSavedMerchantID(); + } + return new Column(children: [ _getLogo(), + _getMerchantIDTitle(), _getDecoratedText(), _getMessage(), _getButton(context) - ])); + ]); } - Container _getLogo() { - return new Container(padding: new EdgeInsets.only(top: 16.0, bottom: 16.0), - child: new Image.asset(logo_png, height: 24.0, width: 156.0)); + _getLogo() { + double containerHeight = 92.0; + double imageWidth = 156.0; + return new Container(height: containerHeight, child: new Image.asset(logo_png, width: imageWidth)); } - Container _getDecoratedText() { + _getMerchantIDTitle() { + return new Container(margin: new EdgeInsets.only(top: 8.0, bottom: 8.0, left: 28.0, right: 28.0), + child: new Row(crossAxisAlignment: CrossAxisAlignment.start, + children: [new Text('ID Магазина', textAlign: TextAlign.left, style: new TextStyle(fontWeight: FontWeight.w300, color: greyTextColor, fontSize: 14.0))])); + } + + _getDecoratedText() { return new Container(margin: new EdgeInsets.only(left: 28.0, right: 28.0), padding: new EdgeInsets.only(top: 12.0, bottom: 12.0, left: 16.0, right: 16.0), decoration: _getDecoraionForMerchantId(), - child: _getMerchantIDText()); + child: new Row(children: [_getMerchantIDText()])); } - Text _getMerchantIDText() { - return new Text(merchantID, style: new TextStyle(color: const Color(0xffa5a5a5), fontSize: 16.0)); + _getMerchantIDText() { + return 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); + }); + }); } Container _getMessage() { - return new Container(padding: new EdgeInsets.only(top: 20.0, left: 26.0, right: 26.0), - child: new Container(height: 128.0, decoration: _getDecoraionForMessageField(), - padding: new EdgeInsets.only(top: 16.0, bottom: 8.0, left: 28.0, right: 28.0), - child: new Text('Запрос на активацию программы отправлен, дождrитесь подтверждения активации администратором', - textAlign: TextAlign.center, style: new TextStyle(fontWeight: FontWeight.bold, color: const Color(0xff4e3a19))))); + 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), + child: new Center(child: new Text(_tokenActive ? 'Программа активирована' : 'Запрос на активацию программы отправлен, дождитесь подтверждения активации администратором', + textAlign: TextAlign.center, style: new TextStyle(height: 1.5, fontWeight: FontWeight.bold, fontSize: 14.0, color: _tokenActive ? tokenActiveTextColor : tokenActivateTextColor)))); } Decoration _getDecoraionForMessageField() { return new BoxDecoration(image: new DecorationImage( - image: new ExactAssetImage(activate_token_bg_png), fit: BoxFit.fill)); + image: new ExactAssetImage(_tokenActive ? active_token_bg_png : activate_token_bg_png), fit: _tokenActive ? BoxFit.fitWidth : BoxFit.fill)); } Decoration _getDecoraionForMerchantId() { - return new BoxDecoration(color: Colors.white, + return new BoxDecoration(color: textFieldBackground, border: new Border.all(color: const Color(0xffcfd8dc), width: 1.0), borderRadius: new BorderRadius.all(new Radius.circular(4.0))); } - Container _getButton(BuildContext context) { - return new Container(padding: new EdgeInsets.only(top: 36.0), - child: new Container(height: 64.0, padding: new EdgeInsets.all(8.0), - child: new RaisedButton(child: new Text('Обновить статус активации', - style: new TextStyle(color: Colors.white)), - onPressed: () { - startScanner(context); - }))); + /// Метод возвращает кнопку, которая запускает отправку токена кассы на сервер. + _getButton(BuildContext context) { + 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 ? 'ЗАВЕРШИТЬ РЕГИСТРАЦИЮ' : 'ОБНОВИТЬ СТАТУС АКТИВАЦИИ', + 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']; + }); + + }).catchError((error) { + print(error.toString()); + return false; + }); + } + }, + color: primaryColor)); } } - -_checkToken(BuildContext context) { - checkToken(context, new CheckTokenCallback()); -} - -class CheckTokenCallback extends Callback { - - call(BuildContext context) { - startScanner(); - } -} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c5bfb1e..6afcbb3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'splash.dart'; import 'registration.dart'; import 'dart:async'; import 'dart:convert'; +import 'purchase.dart'; /// Главный класс приложения. /// Здесь распоосложены константы и некоторые методы, которые могут вызываться с разных экранов приложения. @@ -21,56 +22,62 @@ const String logo_png = 'assets/registration_logo.png'; const String splash_png = 'assets/splash.png'; const String logout_png = 'assets/logout.png'; const String activate_token_bg_png = 'assets/activate_token_message_background.png'; +const String active_token_bg_png = 'assets/active_token_message_background.png'; // Colors const Color primaryColor = const Color(0xffeb0004); -const Color disabledColor = const Color(0xffbfbfbf); +const Color greyTextColor = const Color(0xffa5a5a5); +const Color textBorderColor = const Color(0xffcfd8dc); +const Color textFieldBackground = const Color(0xffefefef); +const Color tokenActiveTextColor = const Color(0xff1f5a1f); +const Color tokenActivateTextColor = const Color(0xff4e3a19); // HttpClient final httpClient = createHttpClient(); -void main() { - runApp(new Checker()); -} - /// Токен кассы. Инициализируется при регистрации. String token; String merchantID = ""; +/// Точка входа в приложение. +void main() { + runApp(new Checker()); +} + /// Проверка статуса токена. Токен может быть активирован, либо не активирован. -void checkToken(BuildContext context, Callback callback) { - - String url = intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken; - print(url); - - httpClient.get(url).then((response) { - - print(response.body); - Map parsedMap = JSON.decode(response.body); - bool active = parsedMap['active']; - - if (!active) { - callback.call(context); - } else { - // Запускается экран сканера, токен кассы активирован, с его помощью можно делать запросы к pos-api. - startScanner(context); - } - - }).catchError((error) { - print(error.toString()); - }); +checkToken(BuildContext context) async { + return httpClient.get(intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken); } /// Запуск спецефичной для каждой платформы части приложения - сканера. /// Может производиться с нескольких экранов (splash, finish_registration). -startScanner(BuildContext context) async{ +startScanner(BuildContext context) async { const platform = const MethodChannel('com.dinect.checker/instance_id'); + + // Канал слушает ловит вызовы методов из "нативной" части приложения. + // Могут быть вызваны либо logaut либо faq, либо purchase. platform.setMethodCallHandler((MethodCall call) async { - pushRoute(context, new RegistrationScreen()); - return result; - // or - // throw new PlatformException(errorCode, anErrorMessage, someDetails); + + if (call.method == 'foo') { + + String url = intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken; + + httpClient.delete(url).then((response) { + + print(response.body); + + }).catchError((error) { + print(error.toString()); + }); + + pushRoute(context, new RegistrationScreen()); + } else { + pushRoute(context, new PurchaseScreen()); + } + + return result; + }); await platform.invokeMethod('startScanner'); @@ -87,15 +94,11 @@ pushRoute(BuildContext context, Widget widget) { class Checker extends StatelessWidget { @override Widget build(BuildContext context) { - return new MaterialApp(title: "DemoApp", + return new MaterialApp(title: "AutoClub", home: new SplashScreen(), theme: new ThemeData( primaryColor: primaryColor, accentColor: primaryColor )); } -} - -abstract class Callback { - void call(BuildContext context); -} +} \ No newline at end of file diff --git a/lib/purchase.dart b/lib/purchase.dart new file mode 100644 index 0000000..4a3ff70 --- /dev/null +++ b/lib/purchase.dart @@ -0,0 +1,101 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'main.dart'; +import 'dart:async'; +import 'activate_token.dart'; + +/// Экран проведения покупки. +class PurchaseScreen extends StatefulWidget { + @override State createState() => new _PurchaseScreenState(); +} + +class _PurchaseScreenState extends State { + + bool _loading = false; + + @override Widget build(BuildContext context) { + return new Scaffold(appBar: _getAppBar(), body: _getScreen(context)); + } + + AppBar _getAppBar() { + return new AppBar(title: new Text("Проведение покупки"), + actions: [new IconButton(icon: new Icon(Icons.help_outline), onPressed: () {})]); + } + + Widget _getScreen(BuildContext context) { + return new Stack(children: [_getScreenContent(), _getProgressIndicator()]); + } + + Widget _getScreenContent() { + return new Container(height: 332.0, + child: new ListView(reverse: true, children: [ + new Column(children: [ + _getValueWithTitle('ФИО', 'Знаменитый Рокер Паук'), + _getValueWithTitle('Карта', 'B0399900702'), + _getValueWithTitle('Вознаграждение', '100%'), + _getButton(context), + _getButton(context)]) + ].reversed.toList())); + } + + Widget _getProgressIndicator() { + return new Center(child: _loading ? new CircularProgressIndicator() : null); + } + + Widget _getValueWithTitle(String title, String value) { + return 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 _getButton(BuildContext context) { + return new Container(margin: new EdgeInsets.only(top: 36.0), height: 42.0, + padding: new EdgeInsets.only(left: 40.0, right: 40.0), + child: new RaisedButton(child: new Text('ЗАРЕГИСТРИРОВАТЬ', + style: new TextStyle(color: Colors.white)), + onPressed: null, + color: primaryColor)); + } + + Widget _getCircularProgressIndicator() { + return new Center(child: new CircularProgressIndicator()); + } + + _register(BuildContext context) async { + // const platform = const MethodChannel('com.dinect.checker/instance_id'); + // String url = intUrl + 'tokens/?_dmapptoken=' + intToken; + // String pos = await platform.invokeMethod('getInstanceID'); + // print(pos); + // String userAgent = 'dm-checker-test v1.0.1'; + + // var body = { + // 'merchant_shop': merchantShop, + // 'pos': pos, + // 'description': userAgent + '-' + pos + // }; + + // print(url); + + // for (var value in body.values) { + // print(value); + // } + + // httpClient.post(url, body: body).then((response) { + // print(response.body); + // Map parsedMap = JSON.decode(response.body); + // token = parsedMap['token']; + // platform.invokeMethod('saveToken', {'token' : token}).then((value) { + // print(value.toString()); + // }); + // setState(() { + // loading = false; + // }); + // pushRoute(context, new FinishRegistrationScreen()); + // }).catchError((error) { + // print(error.toString()); + // }); + } +} \ No newline at end of file diff --git a/lib/registration.dart b/lib/registration.dart index 3c447bd..e620ee2 100644 --- a/lib/registration.dart +++ b/lib/registration.dart @@ -1,10 +1,12 @@ -import 'dart:convert'; +import 'dart:convert'; // Пакет для обработки json с ответом от сервера. import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'main.dart'; import 'dart:async'; import 'activate_token.dart'; +/// На фото мой сын, большой любитель голых констант. + /// Экран регистрации магазина и кассы. class RegistrationScreen extends StatefulWidget { @override State createState() => new _RegistrationScreenState(); @@ -12,92 +14,125 @@ class RegistrationScreen extends StatefulWidget { class _RegistrationScreenState extends State { - String _merchantID = ""; + String error = null; bool _loading = false; - @override Widget build(BuildContext context) { + @override build(BuildContext context) { return new Scaffold(appBar: _getAppBar(), body: _getScreen(context)); } - AppBar _getAppBar() { + _getAppBar() { return new AppBar(title: new Text("Регистрация"), actions: [new IconButton(icon: new Icon(Icons.help_outline), onPressed: () {})]); } - Widget _getScreen(BuildContext context) { + _getScreen(BuildContext context) { return new Stack(children: [_getScreenContent(), _getProgressIndicator()]); } - Widget _getScreenContent() { + /// Высота контейнера задана для того, чтобы элементы располагались вверху экрана + /// и список скроллился снизу вверх при открытии клавиатуры. + _getScreenContent() { return new Container(height: 332.0, - child: new ListView(reverse: true, children: [ - new Center(child: new Column(children: [ - _getLogo(), - _getDecoratedInputField(), - _getButton(context)])) - ].reversed.toList())); + child: new ListView(reverse: true, children: [ + new Center(child: new Column(children: [ + _getLogo(), + _getMerchantIDTitle(), + _getDecoratedInputField(), + _getButton(context)])) + ].reversed.toList())); } - Widget _getProgressIndicator() { + /// Индикация отправки токена кассы. + _getProgressIndicator() { return new Center(child: _loading ? new CircularProgressIndicator() : null); } - Widget _getLogo() { - return new Container(height: 192.0, width: 156.0, - child: new Image.asset(logo_png, height: 24.0, width: 156.0)); + /// Метод возвращает контейнер с отступами, который содержит картинку с логотипом. + /// Картинка должна + _getLogo() { + double containerHeight = 162.0; + double imageHeight = 24.0; + double imageWidth = 156.0; + return new Container(height: containerHeight, child: new Image.asset(logo_png, height: imageHeight, width: imageWidth)); } - Widget _getDecoratedInputField() { - return new Container(margin: new EdgeInsets.only(left: 28.0, right: 28.0), - padding: new EdgeInsets.only(top: 12.0, bottom: 12.0, left: 16.0, right: 16.0), - decoration: _getDecoraionForInputField(), - child: _getInputField()); + _getMerchantIDTitle() { + return new Container(margin: new EdgeInsets.only(top: 8.0, bottom: 8.0, left: 28.0, right: 28.0), + child: new Row(crossAxisAlignment: CrossAxisAlignment.start, + children: [new Container(padding: new EdgeInsets.only(right: 8.0), child: new Text(_getMerchantIDTitleText(), overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, + style: new TextStyle(fontWeight: FontWeight.w300, color: error == null ? greyTextColor : primaryColor, fontSize: 14.0)))])); } - Widget _getInputField() { - return new TextField(decoration: new InputDecoration.collapsed(hintText: merchantIDHint, - hintStyle: new TextStyle(color: const Color(0xffa5a5a5), fontSize: 16.0)), - onChanged: (text) => _handleUserInput(text)); - } - - Decoration _getDecoraionForInputField() { - return new BoxDecoration(color: Colors.white, - border: new Border.all(color: const Color(0xffcfd8dc), width: 1.0,), - borderRadius: new BorderRadius.all(new Radius.circular(4.0))); - } - - Widget _getButton(BuildContext context) { - return new Container(margin: new EdgeInsets.only(top: 36.0), height: 42.0, - padding: new EdgeInsets.only(left: 40.0, right: 40.0), - child: new RaisedButton(child: new Text('ЗАРЕГИСТРИРОВАТЬ', - style: new TextStyle(color: Colors.white)), - onPressed: _isValidMerchantID() ? () => _registerShop(context) : null, - color: primaryColor)); - } - - Widget _getCircularProgressIndicator() { - return new Center(child: new CircularProgressIndicator()); - } - - _isValidMerchantID() { - return merchantID.length == 5; - } - - _handleUserInput(String text) { - if (text.length > 0) { - setState(() { - merchantID = text; - }); + _getMerchantIDTitleText() { + if (merchantID.length == 0 && error == null) { + return ' '; + } else if (error != null) { + return error; + } else { + return 'ID Магазина'; } } - _registerShop(BuildContext context) { + /// Метод возвращает контейнер с установленными отступами, в котором размещен TextField обернутый в BoxDecoration. + _getDecoratedInputField() { + double margin = 28.0; + double verticalPadding = 12.0; + double horizontalPadding = 16.0; + return new Container(margin: new EdgeInsets.only(left: margin, right: margin), + padding: new EdgeInsets.only(top: verticalPadding, bottom: verticalPadding, left: horizontalPadding, right: horizontalPadding), + decoration: _getDecoraionForInputField(), + child: _getInputField()); + } + + /// Метод возвращает TextField для _getDecoratedInputField + _getInputField() { + return new TextField(keyboardType: TextInputType.number, decoration: new InputDecoration.collapsed(hintText: merchantIDHint, + hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)), + onChanged: (text) => _handleUserInput(text)); + } + + /// Метод возвращает BoxDecoration для _getDecoratedInputField + _getDecoraionForInputField() { + return new BoxDecoration(color: textFieldBackground, + border: new Border.all(color: textBorderColor, width: 1.0,), + borderRadius: new BorderRadius.all(new Radius.circular(4.0))); + } + + /// Метод возвращает кнопку, которая запускает отправку токена кассы на сервер. + _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() { + return merchantID.length > 0 && merchantID.length < 25; + } + + /// Смена состояния экрана при изменении текста в поле ввода. + _handleUserInput(String text) { setState(() { - _loading = true; - _registerDemo(context); + merchantID = text; }); } + /// Показать индикатор, запросить токен. + _registerShop(BuildContext context) { + setState(() { + _loading = true; + _register(context); + }); + } + + /// Экран зависает на 1 сек, после этого выполняется переход на экран потверждения токена. _registerDemo(BuildContext context) { new Future.delayed(const Duration(milliseconds: 1000), () { _loading = false; @@ -105,38 +140,44 @@ class _RegistrationScreenState extends State { }); } + /// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена. _register(BuildContext context) async { - // const platform = const MethodChannel('com.dinect.checker/instance_id'); - // String url = intUrl + 'tokens/?_dmapptoken=' + intToken; - // String pos = await platform.invokeMethod('getInstanceID'); - // print(pos); - // String userAgent = 'dm-checker-test v1.0.1'; + const platform = const MethodChannel('com.dinect.checker/instance_id'); - // var body = { - // 'merchant_shop': merchantShop, - // 'pos': pos, - // 'description': userAgent + '-' + pos - // }; + String url = intUrl + 'tokens/?_dmapptoken=' + intToken; + String pos = (new DateTime.now().millisecondsSinceEpoch / 1000).toString(); - // print(url); + // Поле description - необязательное. + var body = { + 'merchant_shop': merchantID, + 'pos': pos, + }; - // for (var value in body.values) { - // print(value); - // } + httpClient.post(url, body: body).then((response) { - // httpClient.post(url, body: body).then((response) { - // print(response.body); - // Map parsedMap = JSON.decode(response.body); - // token = parsedMap['token']; - // platform.invokeMethod('saveToken', {'token' : token}).then((value) { - // print(value.toString()); - // }); - // setState(() { - // loading = false; - // }); - // pushRoute(context, new FinishRegistrationScreen()); - // }).catchError((error) { - // print(error.toString()); - // }); + 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 = 'Отсутствует интернет соединение'; + }); + }); } } \ No newline at end of file diff --git a/lib/splash.dart b/lib/splash.dart index b699e82..d7c8afd 100644 --- a/lib/splash.dart +++ b/lib/splash.dart @@ -1,9 +1,10 @@ import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; +import 'dart:async'; +import 'dart:convert'; import 'main.dart'; import 'registration.dart'; import 'activate_token.dart'; -import 'dart:async'; class SplashScreen extends StatelessWidget { @@ -12,6 +13,7 @@ class SplashScreen extends StatelessWidget { // Splash скрин зависает мимнимум на 1 секунду. // После этого начинается проверка токена. + new Future.delayed(const Duration(milliseconds: 1000), () { _showNextScreen(context); }); @@ -24,25 +26,35 @@ class SplashScreen extends StatelessWidget { const platform = const MethodChannel('com.dinect.checker/instance_id'); token = await platform.invokeMethod('getToken'); - + print('token: $token'); // В случае, если в приложении отсутствует токен, // необходимо запустить регистрацию кассы. - // if (token == null) { + + if (token == null) { pushRoute(context, new RegistrationScreen()); - // } else { - // checkToken(context, new CheckTokenCallback()); - // } - } + } else { -} + checkToken(context).then((response) { -class CheckTokenCallback extends Callback { + print(response.body); + Map parsedMap = JSON.decode(response.body); + bool active = parsedMap['active']; - /// Запускается экран ожидания активации токена. - /// В реальности токен активируется в админке вручную, - /// на тестовом сервере токен активируется через несколько минут после создания. - - call(BuildContext context) { - pushRoute(context, new FinishRegistrationScreen()); + if (active) { + Navigator.of(context).pop(); + // Запускается экран сканера, токен кассы активирован, с его помощью можно делать запросы к pos-api. + startScanner(context); + } else { + // Запускается экран ожидания активации токена. + // В реальности токен активируется в админке вручную, + // на тестовом сервере токен активируется через несколько минут после создания. + pushRoute(context, new FinishRegistrationScreen()); + } + + }).catchError((error) { + print(error.toString()); + return false; + }); + } } } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index ec73eea..3d2d690 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,6 +24,7 @@ flutter: - assets/splash.png - assets/logout.png - assets/activate_token_message_background.png + - assets/active_token_message_background.png # To add assets from package dependencies, first ensure the asset # is in the lib/ directory of the dependency. Then,