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 add943a..5f7a84c 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 @@ -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"); @@ -82,6 +85,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(); @@ -95,9 +102,7 @@ public class CameraActivity extends AppCompatActivity implements SurfaceHolder.C Camera.Parameters params = mCamera.getParameters(); List 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); + } + }; } \ No newline at end of file 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 38f3575..0d1f568 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 @@ -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() { } diff --git a/android/app/src/main/res/layout/a_scanner.xml b/android/app/src/main/res/layout/a_scanner.xml index 6ea9cda..59b524f 100644 --- a/android/app/src/main/res/layout/a_scanner.xml +++ b/android/app/src/main/res/layout/a_scanner.xml @@ -10,38 +10,35 @@ android:layout_width="match_parent" android:layout_height="match_parent"/> - + + + + + { if (_tokenActive) { startScanner(context); } else { - checkToken(context).then((response) { + checkTokenStatus(context).then((response) { print(response.body); Map parsedMap = JSON.decode(response.body); diff --git a/lib/faq.dart b/lib/faq.dart index 01bf97f..f429d47 100644 --- a/lib/faq.dart +++ b/lib/faq.dart @@ -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(); +} - AppBar getAppBar(BuildContext context) { - return new AppBar(title: new Text('FAQ', style: new TextStyle(fontSize: 18.0)), - backgroundColor: primaryColor, actions: [getLogoutButton(context)]); +class FAQScreenState extends BaseState { + + @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 [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, дублируется на почту) Нажать кнопку: «Зарегистрировать» diff --git a/lib/main.dart b/lib/main.dart index 38c3e82..5a4552f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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: [ + 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(); + } + } + )])); + } /// Навигация по приложению. diff --git a/lib/purchase.dart b/lib/purchase.dart index b59f014..7ea43f3 100644 --- a/lib/purchase.dart +++ b/lib/purchase.dart @@ -27,7 +27,7 @@ class PurchaseScreenState extends BaseState { } Map user; - String card; + String card = ''; String integerPart = '', fractionalPart = ''; String loyality = ''; @@ -40,7 +40,7 @@ class PurchaseScreenState extends BaseState { 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 extends BaseState { 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 extends BaseState { 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()); + }); + }); + } } \ No newline at end of file diff --git a/lib/registration.dart b/lib/registration.dart index 29a675d..fe61128 100644 --- a/lib/registration.dart +++ b/lib/registration.dart @@ -54,22 +54,13 @@ class _RegistrationScreenState extends BaseState { /// Получение от платформы 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; loading = false; }); - + print(response.body); Map parsedMap = JSON.decode(response.body); if (response.statusCode == 201) { diff --git a/lib/splash.dart b/lib/splash.dart index 2acbf8b..6c1bd5f 100644 --- a/lib/splash.dart +++ b/lib/splash.dart @@ -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())); + }); + } + } + } } \ No newline at end of file