Добавлена проверка статуса токена, обработка статуса токена
This commit is contained in:
@@ -25,7 +25,7 @@ android {
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
applicationId "com.dinnect.checker"
|
||||
applicationId "com.dinect.checker"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.dinnect.checker"
|
||||
package="com.dinect.checker"
|
||||
android:versionCode="1"
|
||||
android:versionName="0.0.1">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application android:name="io.flutter.app.FlutterApplication" android:label="checker" android:icon="@mipmap/ic_launcher">
|
||||
<activity android:name="com.dinnect.checker.activity.MainActivity"
|
||||
<activity android:name="com.dinect.checker.activity.MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
|
||||
@@ -30,11 +30,11 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.dinnect.checker.activity.CameraActivity"
|
||||
<activity android:name="com.dinect.checker.activity.CameraActivity"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar"/>
|
||||
|
||||
<service
|
||||
android:name="com.dinnect.checker.service.RegistrationIntentService"
|
||||
android:name="com.dinect.checker.service.RegistrationIntentService"
|
||||
android:exported="false"/>
|
||||
|
||||
</application>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.dinnect.checker.activity;
|
||||
package com.dinect.checker.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -29,8 +29,8 @@ import net.sourceforge.zbar.Symbol;
|
||||
import net.sourceforge.zbar.SymbolSet;
|
||||
import net.sourceforge.zbar.Config;
|
||||
|
||||
import com.dinnect.checker.R;
|
||||
import com.dinnect.checker.view.CameraPreview;
|
||||
import com.dinect.checker.R;
|
||||
import com.dinect.checker.view.CameraPreview;
|
||||
|
||||
public class CameraActivity extends Activity {
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.dinnect.checker.activity;
|
||||
package com.dinect.checker.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import com.dinnect.checker.activity.CameraActivity;
|
||||
import com.dinnect.checker.service.RegistrationIntentService;
|
||||
import android.content.SharedPreferences;
|
||||
import com.dinect.checker.activity.CameraActivity;
|
||||
import com.dinect.checker.service.RegistrationIntentService;
|
||||
|
||||
import io.flutter.app.FlutterActivity;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
@@ -16,20 +18,27 @@ import io.flutter.plugin.common.MethodChannel.Result;
|
||||
|
||||
import com.google.android.gms.iid.InstanceID;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
|
||||
private static final String INSTANCE_ID_CHANNEL = "com.dinnect.checker/instance_id";
|
||||
private static final String INSTANCE_ID_CHANNEL = "com.dinect.checker/instance_id";
|
||||
private static final String PREF_POS_TOKEN = "pref_pos_token";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
GeneratedPluginRegistrant.registerWith(this);
|
||||
|
||||
final SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE);
|
||||
|
||||
new MethodChannel(getFlutterView(), INSTANCE_ID_CHANNEL).setMethodCallHandler(
|
||||
new MethodCallHandler() {
|
||||
@Override
|
||||
public void onMethodCall(MethodCall call, Result result) {
|
||||
if (call.method.equals("getInstanceID")) {
|
||||
switch (call.method) {
|
||||
|
||||
case "getInstanceID":
|
||||
|
||||
InstanceID instanceID = InstanceID.getInstance(MainActivity.this);
|
||||
String id = instanceID.getId();
|
||||
@@ -39,9 +48,26 @@ public class MainActivity extends FlutterActivity {
|
||||
} else {
|
||||
result.error("UNAVAILABLE", "Can't get instanceID.", null);
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
case "saveToken":
|
||||
Map arguments = call.arguments();
|
||||
String token = (String) arguments.get("token");
|
||||
Log.d("kifio", token);
|
||||
preferences.edit().putString(PREF_POS_TOKEN, token).apply();
|
||||
break;
|
||||
|
||||
case "getToken":
|
||||
result.success(preferences.getString(PREF_POS_TOKEN, null));
|
||||
break;
|
||||
|
||||
case "startScanner":
|
||||
startActivity(new Intent(MainActivity.this, CameraActivity.class));
|
||||
break;
|
||||
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -55,4 +81,12 @@ public class MainActivity extends FlutterActivity {
|
||||
|
||||
}
|
||||
|
||||
public void saveToken() {
|
||||
|
||||
}
|
||||
|
||||
public void getToken() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.dinnect.checker.service;
|
||||
package com.dinect.checker.service;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.dinnect.checker.view;
|
||||
package com.dinect.checker.view;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
BIN
assets/activate_token_message_background.png
Normal file
BIN
assets/activate_token_message_background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
103
lib/activate_token.dart
Normal file
103
lib/activate_token.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'main.dart';
|
||||
|
||||
/// Экран регистрации магазина и кассы.
|
||||
class FinishRegistrationScreen extends StatefulWidget {
|
||||
@override State createState() => new _RegistrationScreenState();
|
||||
}
|
||||
|
||||
class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
|
||||
@override Widget build(BuildContext context) {
|
||||
return new Scaffold(appBar: _getAppBar(), body: _getScreen(context));
|
||||
}
|
||||
|
||||
AppBar _getAppBar() {
|
||||
return new AppBar(title: new Text("Регистрация магазина"),
|
||||
backgroundColor: const Color(0xff4272e7), actions: <Widget>[
|
||||
new IconButton(
|
||||
icon: new Icon(Icons.help_outline),
|
||||
tooltip: 'Air it',
|
||||
onPressed: faq,
|
||||
),
|
||||
new IconButton(
|
||||
icon: new Image(height: 24.0, width: 24.0, image: new AssetImage(logout_png)),
|
||||
tooltip: 'Restitch it',
|
||||
onPressed: logout,
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _getScreen(BuildContext context) {
|
||||
return new Center(child: new Column(children: <Widget>[
|
||||
_getLogo(),
|
||||
_getDecoratedInputField(merchantIDHint),
|
||||
_getDecoratedInputField(posIDHint),
|
||||
_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));
|
||||
}
|
||||
|
||||
Container _getDecoratedInputField(String hint) {
|
||||
return new Container(
|
||||
padding: new EdgeInsets.only(left: 28.0, right: 28.0, top: 8.0),
|
||||
child: new Container(height: 48.0,
|
||||
padding: new EdgeInsets.only(left: 16.0, right: 16.0),
|
||||
decoration: _getDecoraionForInputField(),
|
||||
child: _getInputField(hint))) ;
|
||||
}
|
||||
|
||||
TextField _getInputField(String hint) {
|
||||
return new TextField(decoration: new InputDecoration(hintText: hint,
|
||||
hideDivider: true,
|
||||
hintStyle: new TextStyle(color: const Color(0xffa5a5a5),
|
||||
fontSize: 16.0)), onChanged: null);
|
||||
}
|
||||
|
||||
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('Запрос на активацию программы отправлен, дождитесь подтверждения активации администратором',
|
||||
textAlign: TextAlign.center, style: new TextStyle(fontWeight: FontWeight.bold, color: const Color(0xff4e3a19)))));
|
||||
}
|
||||
|
||||
Decoration _getDecoraionForMessageField() {
|
||||
return new BoxDecoration(image: new DecorationImage(
|
||||
image: new ExactAssetImage(activate_token_bg_png), fit: BoxFit.fill));
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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: _checkToken(context),
|
||||
disabledColor: const Color(0xffbfbfbf),
|
||||
color: const Color(0xff3078c0))));
|
||||
}
|
||||
}
|
||||
|
||||
_checkToken(BuildContext context) {
|
||||
checkToken(context, new CheckTokenCallback());
|
||||
}
|
||||
|
||||
class CheckTokenCallback extends Callback {
|
||||
|
||||
call(BuildContext context) {
|
||||
startScanner();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'splash.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
const String _name = 'Ivan Murashov';
|
||||
/// Главный класс приложения.
|
||||
/// Здесь распоосложены константы и некоторые методы, которые могут вызываться с разных экранов приложения.
|
||||
|
||||
// Serious constants
|
||||
const String intUrl = 'https://pos-api-int.dinect.com/20130701/';
|
||||
const String intToken = '9fec83cdca38c357e6b65dbb17514cdd36bf2a08';
|
||||
|
||||
// Hints
|
||||
const String merchantIDHint = 'ID магазина';
|
||||
const String posIDHint = 'Номер кассы';
|
||||
|
||||
// Assets
|
||||
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';
|
||||
|
||||
final httpClient = createHttpClient();
|
||||
|
||||
void main() {
|
||||
runApp(new Checker());
|
||||
}
|
||||
|
||||
/// Токен кассы. Инициализируется при регистрации.
|
||||
String token;
|
||||
|
||||
/// Проверка статуса токена. Токен может быть активирован, либо не активирован.
|
||||
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();
|
||||
}
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
/// Запуск спецефичной для каждой платформы части приложения - сканера.
|
||||
/// Может производиться с нескольких экранов (splash, finish_registration).
|
||||
startScanner() async{
|
||||
const platform = const MethodChannel('com.dinect.checker/instance_id');
|
||||
await platform.invokeMethod('startScanner');
|
||||
}
|
||||
|
||||
/// Навигация по приложению.
|
||||
/// widget - следующий экран приложения.
|
||||
pushRoute(BuildContext context, Widget widget) {
|
||||
Navigator.of(context).push(new MaterialPageRoute<Null>(
|
||||
builder: (BuildContext context) {
|
||||
return widget;
|
||||
}));
|
||||
}
|
||||
|
||||
class Checker extends StatelessWidget {
|
||||
@override Widget build(BuildContext context) {
|
||||
return new MaterialApp(title: "DemoApp", home: new SplashScreen());
|
||||
@@ -28,3 +86,7 @@ abstract class BaseState<T> extends State<StatefulWidget> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Callback {
|
||||
void call(BuildContext context);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'main.dart';
|
||||
import 'activate_token.dart';
|
||||
|
||||
/// Экран регистрации магазина и кассы.
|
||||
class RegistrationScreen extends StatefulWidget {
|
||||
@@ -9,14 +11,11 @@ class RegistrationScreen extends StatefulWidget {
|
||||
|
||||
class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
|
||||
static const String _merchantIDHint = 'ID магазина';
|
||||
static const String _posIDHint = 'Номер кассы';
|
||||
|
||||
String _merchantID = "";
|
||||
String _posID = "";
|
||||
|
||||
@override Widget build(BuildContext context) {
|
||||
return new Scaffold(appBar: _getAppBar(), body: _getScreen());
|
||||
return new Scaffold(appBar: _getAppBar(), body: _getScreen(context));
|
||||
}
|
||||
|
||||
AppBar _getAppBar() {
|
||||
@@ -35,25 +34,19 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _getScreen() {
|
||||
return new Center(child: new Column(children: _getChildren()));
|
||||
Widget _getScreen(BuildContext context) {
|
||||
return new Center(child: new Column(children: <Widget>[
|
||||
_getLogo(),
|
||||
_getDecoratedInputField(merchantIDHint, 0.0),
|
||||
_getDecoratedInputField(posIDHint, 36.0),
|
||||
_getButton(context)
|
||||
]));
|
||||
}
|
||||
|
||||
List<Widget> _getChildren() {
|
||||
return<Widget>[
|
||||
_getLogo(),
|
||||
_getDecoratedInputField(_merchantIDHint, 0.0),
|
||||
_getDecoratedInputField(_posIDHint, 36.0),
|
||||
_getButton()
|
||||
];
|
||||
}
|
||||
|
||||
Container _getLogo() {
|
||||
return new Container(height: 192.0,
|
||||
child: new Center(
|
||||
child: new Image.asset('assets/registration_logo.png',
|
||||
height: 24.0,
|
||||
width: 156.0)));
|
||||
child: new Image.asset(logo_png, height: 24.0, width: 156.0));
|
||||
}
|
||||
|
||||
Container _getDecoratedInputField(String hint, double topPadding) {
|
||||
@@ -75,9 +68,9 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
void _handleUserInput(String hint, String text) {
|
||||
if (text.length > 0) {
|
||||
setState(() {
|
||||
if (hint == _merchantIDHint)
|
||||
if (hint == merchantIDHint)
|
||||
_merchantID = text;
|
||||
else if (hint == _posIDHint)
|
||||
else if (hint == posIDHint)
|
||||
_posID = text;
|
||||
});
|
||||
}
|
||||
@@ -90,13 +83,14 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
borderRadius: new BorderRadius.all(new Radius.circular(4.0)));
|
||||
}
|
||||
|
||||
Container _getButton() {
|
||||
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: _isFieldsAreFilled() ? () => _register(_merchantID) : null,
|
||||
disabledColor: const Color(0xffbfbfbf))));
|
||||
onPressed: _isFieldsAreFilled() ? () => _registerShop(context, _merchantID) : null,
|
||||
disabledColor: const Color(0xffbfbfbf),
|
||||
color: const Color(0xff3078c0))));
|
||||
}
|
||||
|
||||
_isFieldsAreFilled() {
|
||||
@@ -105,9 +99,13 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
return _merchantID.length == 5 && _posID.length > 0;
|
||||
}
|
||||
|
||||
_register(String merchantShop) async {
|
||||
void _registerShop(BuildContext context, String merchantShop) {
|
||||
_register(context, merchantShop);
|
||||
}
|
||||
|
||||
const platform = const MethodChannel('com.dinnect.checker/instance_id');
|
||||
_register(BuildContext context, String merchantShop) async {
|
||||
|
||||
const platform = const MethodChannel('com.dinect.checker/instance_id');
|
||||
String url = intUrl + 'tokens/?_dmapptoken=' + intToken;
|
||||
String pos = await platform.invokeMethod('getInstanceID');
|
||||
print(pos);
|
||||
@@ -127,6 +125,12 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
|
||||
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());
|
||||
});
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
|
||||
@@ -1,24 +1,47 @@
|
||||
import 'dart:async';
|
||||
import 'registration.dart';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'main.dart';
|
||||
import 'registration.dart';
|
||||
import 'activate_token.dart';
|
||||
import 'dart:async';
|
||||
|
||||
class SplashScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// Splash скрин зависает мимнимум на 1 секунду.
|
||||
// После этого начинается проверка токена.
|
||||
new Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
_goToNextScreen(context);
|
||||
_showNextScreen(context);
|
||||
});
|
||||
|
||||
return new Image.asset('assets/splash.png', fit: BoxFit.cover);
|
||||
return new Image.asset(logo_png, fit: BoxFit.cover);
|
||||
}
|
||||
|
||||
_goToNextScreen(BuildContext context) async {
|
||||
Navigator.of(context).push(new MaterialPageRoute<Null>(
|
||||
builder: (BuildContext context) {
|
||||
return new RegistrationScreen();
|
||||
}));
|
||||
/// Запуск следующего экрана приложения.
|
||||
_showNextScreen(BuildContext context) async {
|
||||
|
||||
const platform = const MethodChannel('com.dinect.checker/instance_id');
|
||||
token = await platform.invokeMethod('getToken');
|
||||
|
||||
// В случае, если в приложении отсутствует токен,
|
||||
// необходимо запустить регистрацию кассы.
|
||||
if (token == null) {
|
||||
pushRoute(context, new RegistrationScreen());
|
||||
} else {
|
||||
checkToken(context, new CheckTokenCallback());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CheckTokenCallback extends Callback {
|
||||
|
||||
/// Запускается экран ожидания активации токена.
|
||||
/// В реальности токен активируется в админке вручную,
|
||||
/// на тестовом сервере токен активируется через несколько минут после создания.
|
||||
call(BuildContext context) {
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ flutter:
|
||||
- assets/registration_logo.png
|
||||
- assets/splash.png
|
||||
- assets/logout.png
|
||||
- assets/activate_token_message_background.png
|
||||
|
||||
# To add assets from package dependencies, first ensure the asset
|
||||
# is in the lib/ directory of the dependency. Then,
|
||||
|
||||
Reference in New Issue
Block a user