Проведение покупки, проверка деактивированного токена, подтверждение логаута, функциональная маска на сканере, кнопка назад в faq

This commit is contained in:
Ivan Murashov
2017-07-26 18:57:29 +03:00
parent 313dc307dc
commit 73e139f925
10 changed files with 226 additions and 112 deletions

View File

@@ -45,6 +45,7 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.lang.Exception;
import java.util.List;
import com.dinect.checker.R;
@@ -57,6 +58,8 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
private boolean mBarcodeScanned = false;
private boolean previewing = true;
private Handler autoFocusHandler;
private int mOffset;
static {
System.loadLibrary("iconv");
@@ -83,6 +86,10 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mOffset = (int) (56 * getResources().getDisplayMetrics().density);
autoFocusHandler = new Handler();
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
@@ -95,9 +102,7 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
Camera.Parameters params = mCamera.getParameters();
List<String> focusModes = params.getSupportedFocusModes();
for (String fMode : focusModes) {
Log.d("kifio", fMode);
}
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
@@ -132,6 +137,12 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
try {
mCamera.autoFocus(autoFocusCallback);
} catch(Exception e) {
e.printStackTrace();
}
Log.d("kifio", "surfaceCreated");
} catch (IOException e) {
e.printStackTrace();
@@ -147,6 +158,7 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d("kifio", "surfaceDestroyed");
mCamera.cancelAutoFocus();
}
void setCameraDisplayOrientation(int cameraId) {
@@ -209,24 +221,6 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
finish();
}
private void initCountours() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
Resources res = getResources();
mContours.put("left", (int) res.getDimension(R.dimen.scanner_contour_top));
mContours.put("top", (int) res.getDimension(R.dimen.scanner_contour_left));
mContours.put("width", (int) res.getDimension(R.dimen.scanner_contour_height));
mContours.put("height", width - (2 * (int) res.getDimension(R.dimen.scanner_contour_left)));
// Log.d("kifio", "left: " + mContours.get("left"));
// Log.d("kifio", "top: " + mContours.get("top"));
// Log.d("kifio", "width: " + mContours.get("width"));
// Log.d("kifio", "height: " + mContours.get("height"));
}
PreviewCallback previewCallback = new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
@@ -235,7 +229,8 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
// barcode.setCrop(mContours.get("left"), mContours.get("top"), mContours.get("width"), mContours.get("height"));
barcode.setCrop((size.width / 2) - mOffset, 0, (size.width / 2) + mOffset, size.height);
int result = mScanner.scanImage(barcode);
@@ -255,4 +250,18 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C
}
}
};
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (previewing)
mCamera.autoFocus(autoFocusCallback);
}
};
AutoFocusCallback autoFocusCallback = new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
}

View File

@@ -15,12 +15,15 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import com.google.android.gms.iid.InstanceID;
import java.util.Map;
import java.lang.System;
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 static final String PREF_DOC_ID = "pref_doc_id";
private static final String PREF_POS_ID = "pref_pos_id";
private MethodChannel mChannel;
private SharedPreferences mPreferences;
@@ -69,6 +72,28 @@ public class MainActivity extends FlutterActivity {
case "removeKeys":
mPreferences.edit().remove(PREF_POS_TOKEN).apply();
mPreferences.edit().remove(PREF_POS_MERCHANT_ID).apply();
mPreferences.edit().remove(PREF_DOC_ID).apply();
mPreferences.edit().remove(PREF_POS_ID).apply();
result.success(null);
break;
case "getDocID":
int docId = mPreferences.getInt(PREF_DOC_ID, 0) + 1;
mPreferences.edit().putInt(PREF_DOC_ID, docId).apply();
result.success(String.valueOf(docId));
break;
case "getPosID":
String posId = mPreferences.getString(PREF_POS_ID, null);
if (posId == null) {
posId = String.valueOf(System.currentTimeMillis());
}
Log.d("kifio", posId);
mPreferences.edit().putString(PREF_POS_ID, posId).apply();
result.success(posId);
break;
default:
result.notImplemented();
@@ -96,6 +121,10 @@ public class MainActivity extends FlutterActivity {
}
}
private void getDocID() {
}
private void removeKeys() {
}
@@ -116,6 +145,10 @@ public class MainActivity extends FlutterActivity {
}
public void getPosID() {
}
public void saveMerchantID() {
}

View File

@@ -10,38 +10,35 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- <LinearLayout android:orientation="vertical"
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize">
android:layout_height="match_parent">
<View android:layout_height="0dp"
android:layout_weight="0.42"
android:layout_weight="0.5"
android:layout_width="match_parent"
android:background="#с0000000"/>
<FrameLayout android:layout_height="0dp"
android:layout_weight="0.16"
android:layout_width="match_parent">
<View android:layout_height="match_parent"
android:layout_width="24dp"
android:layout_gravity="left"
android:background="#с0000000"/>
<View android:layout_height="match_parent"
android:layout_width="24dp"
android:layout_gravity="right"
android:background="#с0000000"/>
</FrameLayout>
android:background="#с0000000"
android:layout_marginBottom="56dp"/>
<View android:layout_height="0dp"
android:layout_weight="0.42"
android:layout_weight="0.5"
android:layout_width="match_parent"
android:background="#с0000000"/>
android:background="#с0000000"
android:layout_marginTop="56dp"/>
</LinearLayout> -->
</LinearLayout>
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="center"
android:background="#00ff00"
android:layout_marginBottom="56dp"/>
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="center"
android:background="#00ff00"
android:layout_marginTop="56dp"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"

View File

@@ -42,7 +42,7 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
if (_tokenActive) {
startScanner(context);
} else {
checkToken(context).then((response) {
checkTokenStatus(context).then((response) {
print(response.body);
Map parsedMap = JSON.decode(response.body);

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'main.dart';
import 'base_state.dart';
/// Класс содержит заголовки и текст блоков FAQ.
class Entry {
@@ -33,24 +34,22 @@ class EntryItem extends StatelessWidget {
}
}
/// Экран проведения покупки.
class FAQScreen extends StatelessWidget {
class FAQScreen extends StatefulWidget {
@override State createState() => new FAQScreenState<FAQScreen>();
}
AppBar getAppBar(BuildContext context) {
return new AppBar(title: new Text('FAQ', style: new TextStyle(fontSize: 18.0)),
backgroundColor: primaryColor, actions: <Widget>[getLogoutButton(context)]);
class FAQScreenState<T> extends BaseState<FAQScreen> {
@override String getTitle() {
return "FAQ";
}
getLogoutButton(BuildContext context) {
return new IconButton(icon: new Image.asset(logout_png, height: iconHeight, width: iconHeight), onPressed: () => logout(context));
}
@override
Widget build(BuildContext context) {
return new MaterialApp(home: new Scaffold(appBar: getAppBar(context), body: getBody()));
@overide getMenuButtons(BuildContext context) {
return <Widget>[getLogoutButton()];
}
/// Метод возвращает ListView с блоками faq.
getBody() {
@override Widget getScreenContent() {
return new ListView.builder(
itemBuilder: (BuildContext context, int index) => new EntryItem(data[index]),
itemCount: data.length);
@@ -64,8 +63,6 @@ class FAQScreen extends StatelessWidget {
new Entry('ОБЩАЯ ИНФОРМАЦИЯ', commonGuide)
];
/// TODO: Отформатировать строки
static const String registrationGuide = '''После запуска приложения вы окажетесь на странице регистрации магазина.
Введите DIN код магазина (выдается партнером/менеджером International Auto Club, дублируется на почту)
Нажать кнопку: «Зарегистрировать»

View File

@@ -55,10 +55,28 @@ void main() {
}
/// Проверка статуса токена. Токен может быть активирован, либо не активирован.
checkToken(BuildContext context) async {
checkTokenStatus(BuildContext context) async {
return httpClient.get(intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken);
}
createToken(String merchantId) async {
String url = intUrl + 'tokens/?_dmapptoken=' + intToken;
String posID = await platform.invokeMethod('getPosID');
print('posID: ${posID}');
print('merchantId: ${merchantId}');
// Поле description - необязательное.
var body = {
'merchant_shop': merchantId,
'pos': posID,
};
return httpClient.post(url, body: body);
}
/// Запуск спецефичной для каждой платформы части приложения - сканера.
/// Может производиться с нескольких экранов (splash, finish_registration).
startScanner(BuildContext context) async {
@@ -88,7 +106,7 @@ startScanner(BuildContext context) async {
print(response.body);
List usersList = JSON.decode(response.body);
print('usersList.length: ${usersList.length}');
if (usersList.length > 0) {
pushRoute(context, new PurchaseScreen(usersList[0], card));
}
@@ -105,21 +123,38 @@ startScanner(BuildContext context) async {
}
logout(BuildContext context) {
if (token != null) {
String url = intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken;
print(url);
httpClient.delete(url).then((response) {
print(response.body);
const platform = const MethodChannel('com.dinect.checker/instance_id');
platform.invokeMethod('removeKeys');
Navigator.of(context).pop();
pushRoute(context, new RegistrationScreen());
}).catchError((error) {
print(error.toString());
});
} else {
Navigator.of(context).pop();
}
showDialog(context: context, child: new AlertDialog(
title: new Text('Подтверждение'),
content: new Text('Вы действительно хотите выйти и ввести другой номер магазина?'),
actions: <Widget>[
new FlatButton(
child: new Text('Нет'),
onPressed: () {
Navigator.of(context).pop();
}
),
new FlatButton(
child: new Text('Да'),
onPressed: () {
if (token != null) {
String url = intUrl + 'tokens/' + token + '?_dmapptoken=' + intToken;
print(url);
httpClient.delete(url).then((response) async {
print(response.body);
await platform.invokeMethod('removeKeys');
Navigator.of(context).pop();
Navigator.of(context).pop();
pushRoute(context, new RegistrationScreen());
}).catchError((error) {
print(error.toString());
});
} else {
Navigator.of(context).pop();
Navigator.of(context).pop();
}
}
)]));
}
/// Навигация по приложению.

View File

@@ -27,7 +27,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
}
Map user;
String card;
String card = '';
String integerPart = '', fractionalPart = '';
String loyality = '';
@@ -40,7 +40,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
getValueWithTitle('Вознаграждение', loyality),
getHintLabel(),
getDecoratedTextWidget(),
buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildRaisedButton(context, 'ЗАВЕРШИТЬ ПОКУПКУ', () => _purchase(context))),
buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildRaisedButton(context, 'ЗАВЕРШИТЬ ПОКУПКУ', () => onPurchaseClick(context))),
buildButton(new EdgeInsets.only(top: 24.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildFlatButton(context, 'СКАНИРОВАТЬ', primaryColor))])
].reversed.toList()));
}
@@ -123,7 +123,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
return temporaryInteger + '.' + temporaryFractional;
}
_purchase(BuildContext context) {
onPurchaseClick(BuildContext context) {
String val = _buildSum();
print(val);
showDialog(context: context, child: new AlertDialog(
@@ -139,10 +139,39 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
new FlatButton(
child: new Text('Да'),
onPressed: () {
Navigator.of(context).pop();
pushRoute(context, new PurchaseSuccessScreen(val, user['first_name']));
purchase(val);
},
)
]));
}
purchase(String sum_total) {
platform.invokeMethod('getDocID').then((result) {
String url = user['purchases_url'];
var body = {
'doc_id': result,
'curr_iso_code': '643',
'commit': 'true',
'sum_total': sum_total
};
var headers = {
'DM-Authorization': 'dmapptoken 9fec83cdca38c357e6b65dbb17514cdd36bf2a08',
'Authorization': 'dmtoken ${token}'
};
httpClient.post(url, body: body, headers: headers).then((response) {
print(response.body);
Navigator.of(context).pop();
pushRoute(context, new PurchaseSuccessScreen(sum_total, user['first_name']));
}).catchError((error) {
print(error.toString());
});
});
}
}

View File

@@ -54,16 +54,7 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
/// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена.
_register(BuildContext context) async {
String url = intUrl + 'tokens/?_dmapptoken=' + intToken;
String pos = (new DateTime.now().millisecondsSinceEpoch / 1000).toString();
// Поле description - необязательное.
var body = {
'merchant_shop': textFieldValue,
'pos': pos,
};
httpClient.post(url, body: body).then((response) {
createToken(textFieldValue).then((response) {
setState(() {
error = null;

View File

@@ -42,27 +42,50 @@ class SplashScreen extends StatelessWidget {
pushRoute(context, new RegistrationScreen());
} else {
checkToken(context).then((response) {
print(response.body);
Map parsedMap = JSON.decode(response.body);
bool active = parsedMap['active'];
if (active) {
// Запускается экран сканера, токен кассы активирован, с его помощью можно делать запросы к pos-api.
startScanner(context);
} else {
// Запускается экран ожидания активации токена.
// В реальности токен активируется в админке вручную,
// на тестовом сервере токен активируется через несколько минут после создания.
pushRoute(context, new FinishRegistrationScreen());
}
checkTokenStatus(context).then((statusResponse) {
handleStatusResponse(context, statusResponse);
}).catchError((error) {
print('Handle exception!');
print(error.toString());
return false;
});
}
}
startRegistration() async {
}
handleStatusResponse(BuildContext context, var statusResponse) async {
int code = statusResponse.statusCode;
print('resp: ${code}');
if (code == 404) {
platform.invokeMethod('removeKeys').then((result) {
print('try to start registration');
pushRoute(context, new RegistrationScreen());
});
} else {
Map statusResponseMap = JSON.decode(statusResponse.body);
bool active = statusResponseMap['active'];
if (active) {
startScanner(context);
} else {
createToken(await platform.invokeMethod('getMerchantID')).then((response) {
print('response.body: ${response.body}');
if (response.statusCode == 409) {
pushRoute(context, new FinishRegistrationScreen());
} else {
startRegistration();
}
}).catchError((error) {
platform.invokeMethod('removeKeys').then((result) => pushRoute(context, new RegistrationScreen()));
});
}
}
}
}