Merge branch 'RG-3445'
60
README.md
@@ -1,12 +1,66 @@
|
||||
Приложение Checker.
|
||||
#Приложение Checker.
|
||||
|
||||
Для запуска необходимо установить [Dart](https://www.dartlang.org/install) - язык программирования и
|
||||
[flutter](https://flutter.io/setup/) - фреймворк для создания кроссплатформенных мобильных приложений на этом языке.
|
||||
|
||||
# Перед тем, как собирать приложение, необходимо в файле lib/consts.dart установить правильное значение appName. Для автоклуба - это AutoBonus.
|
||||
|
||||
Для сборки и запуска приложения используются команды flutter run (собирает debug apk, устанавливает его на устройство) и
|
||||
flutter build (собирает release apk, не устанавливает на устрйоство).
|
||||
flutter build (собирает release apk, не устанавливает на устройство).
|
||||
|
||||
Команды run и build необходимо выполнять с опцией --flavor, чтобы apk файл собирался с необходимыми ресурсами и настройками.
|
||||
Название конкретной flavor передается в аргументе. Все flavors перечислены в файле android/app/build.gradle.
|
||||
|
||||
#Добавление брендированного приложения
|
||||
|
||||
Для добавления брендированного приложения с названием %name% необходимо:
|
||||
|
||||
1) В каталог assets/ положить изображения %name%_logo.png и %name%_splash.png
|
||||
В качестве splash очень желательно использовать квадрат белого цвета 100x100.
|
||||
|
||||
2) В файл pubscpec.yaml в раздел assets/ добавить пути этих изображений.
|
||||
|
||||
3) В файл lib/resources.dart в методы ```getPrimaryColor``` и ```getButtonsColor```
|
||||
добавить цвета необходимые цвета.*
|
||||
|
||||
4) В файле consts.dart изменить значения переменных appName, url, appToken на правильные для сборки значения.*
|
||||
|
||||
5) В файл android/app/build.gradle в раздел productFlavors добавить блок следующего вида:
|
||||
```
|
||||
%name% {
|
||||
applicationId 'com.dinect.autobonus'
|
||||
buildConfigField "String", "locale", "\"ru\""
|
||||
buildConfigField "String", "flavor", "\"%name%\""
|
||||
buildConfigField "int", "currency", "643"
|
||||
buildConfigField "String", "supportPhone", "\"8-800-234-6064\""
|
||||
buildConfigField "String", "supportUrl", "\"https://www.auto-club.biz\""
|
||||
}
|
||||
```
|
||||
|
||||
где все параметры необходимо заменить на соответствующие приложению значения.
|
||||
|
||||
6) В каталог android/app/src/ добавить каталоги %name%/res в которых воссоздать структуру ресурсов аналогичную представленной в каталоге android/app/src/pip/res/:
|
||||
Каталоги mipmap должны содержать иконки, каталоги values должны содержать .xml файлы с названием приложения в следующем формате:
|
||||
```
|
||||
<resources>
|
||||
<string name="app_name">%name%</string>
|
||||
</resources>
|
||||
```
|
||||
|
||||
|
||||
Иконки проще всего нарезать тут:
|
||||
https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html
|
||||
|
||||
В качестве Foreground выбрать иконку приложения размером 512x512, выставить необходимые параметры и скачать архив с нарещанными иконками.
|
||||
|
||||
После выполнения всех этих пунктов появится возможность собирать приложение
|
||||
как описано выше(flutter run --flavor %name% либо flutter build apk --flavor %name%).
|
||||
|
||||
\* - параметры из этих пунктов будут при первой же возможности перенесены в пункт 5, чтобы менять их из одного места.
|
||||
|
||||
#Добавление локализации приложения
|
||||
|
||||
1) В каталог lib/i18n добавить файл messages_%locale%.dart.
|
||||
Файл делать по аналогии с messages_en.dart. Либо с messages_ru.dart.
|
||||
|
||||
2) В каталог android/app/src/main/res добавить каталог values-%locale% с единственным файлом strings.xml.
|
||||
Файл должен иметь структуру полностью аналогичную файлу android/app/src/main/res/values/strings.xml, измениться должны только значения для строк.
|
||||
@@ -42,40 +42,22 @@ android {
|
||||
|
||||
productFlavors {
|
||||
|
||||
autobonus_en {
|
||||
applicationId 'com.dinect.autobonus'
|
||||
buildConfigField "String", "locale", "\"en\""
|
||||
buildConfigField "String", "flavor", "\"autobonus\""
|
||||
}
|
||||
|
||||
autobonus_ru {
|
||||
autobonus {
|
||||
applicationId 'com.dinect.autobonus'
|
||||
buildConfigField "String", "locale", "\"ru\""
|
||||
buildConfigField "String", "flavor", "\"autobonus\""
|
||||
buildConfigField "int", "currency", "643"
|
||||
buildConfigField "String", "supportPhone", "\"8-800-234-6064\""
|
||||
buildConfigField "String", "supportUrl", "\"https://www.auto-club.biz\""
|
||||
}
|
||||
|
||||
autobonus_ua {
|
||||
applicationId 'com.dinect.autobonus'
|
||||
buildConfigField "String", "locale", "\"ua\""
|
||||
buildConfigField "String", "flavor", "\"autobonus\""
|
||||
}
|
||||
|
||||
pip_en {
|
||||
applicationId 'com.dinect.pip'
|
||||
buildConfigField "String", "locale", "\"en\""
|
||||
buildConfigField "String", "flavor", "\"pip\""
|
||||
}
|
||||
|
||||
pip_ru {
|
||||
applicationId 'com.dinect.pip'
|
||||
buildConfigField "String", "locale", "\"ru\""
|
||||
buildConfigField "String", "flavor", "\"pip\""
|
||||
}
|
||||
|
||||
pip_ua {
|
||||
pip {
|
||||
applicationId 'com.dinect.pip'
|
||||
buildConfigField "String", "locale", "\"ua\""
|
||||
buildConfigField "String", "flavor", "\"pip\""
|
||||
buildConfigField "int", "currency", "980"
|
||||
buildConfigField "String", "supportPhone", "\"+38 080 030 9997\\n+38 044 390 1697\""
|
||||
buildConfigField "String", "supportUrl", "\"http://discount.kiev.ua/\""
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,20 +66,11 @@ android {
|
||||
|
||||
main.jniLibs.srcDir 'jniLibs'
|
||||
|
||||
pip_ua {
|
||||
pip {
|
||||
res.srcDirs = ['src/pip/res']
|
||||
manifest.srcFile 'src/pip/AndroidManifest.xml'
|
||||
}
|
||||
|
||||
pip_ru {
|
||||
res.srcDirs = ['src/pip/res']
|
||||
manifest.srcFile 'src/pip/AndroidManifest.xml'
|
||||
}
|
||||
|
||||
pip_en {
|
||||
res.srcDirs = ['src/pip/res']
|
||||
manifest.srcFile 'src/pip/AndroidManifest.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.dinect.checker"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0.1">
|
||||
android:versionName="1.1.0">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
@@ -31,7 +31,7 @@
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||
android:theme="@android:style/Theme.Light.NoTitleBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
@@ -23,27 +23,36 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dinect.checker.net.ApiClient;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
|
||||
/**
|
||||
@@ -54,26 +63,13 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
|
||||
private final static String TAG = "Checker.ScannerActivity";
|
||||
|
||||
private static final String[] CLICK_MESSAGES = {
|
||||
"Там ничего нет.",
|
||||
"Зачем ты это делаешь?",
|
||||
"Перестань!",
|
||||
"Ну и зачем?..",
|
||||
};
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
private int mColor;
|
||||
private int counter;
|
||||
|
||||
public static final String SCAN_MODES = "SCAN_MODES";
|
||||
public static final String ERROR_INFO = "ERROR_INFO";
|
||||
|
||||
|
||||
protected AppCompatActivity ctx;
|
||||
protected ApiClient apiClient;
|
||||
protected NetworkThread networkThread;
|
||||
protected DecrementCounterThread counterThread;
|
||||
protected LogoutDialogFragment logoutDialog;
|
||||
protected NotificationThread notificationThread;
|
||||
private ApiClient mClient;
|
||||
|
||||
boolean isCameraAvailable() {
|
||||
Log.d(TAG, "isCameraAvailable");
|
||||
@@ -86,7 +82,15 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
final Intent response = new Intent();
|
||||
response.putExtra(ERROR_INFO, message);
|
||||
setResult(RESULT_CANCELED, response);
|
||||
notificationThread = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final String appToken = getIntent().getStringExtra(MainActivity.PREF_APP_TOKEN);
|
||||
final String token = getIntent().getStringExtra(MainActivity.PREF_POS_TOKEN);
|
||||
final String url = getIntent().getStringExtra(MainActivity.PREF_API_URL) + "/users/";
|
||||
mClient = new ApiClient(url, appToken, token);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,9 +107,6 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
cancelRequest("Camera unavailable");
|
||||
return false;
|
||||
}
|
||||
ctx = this;
|
||||
notificationThread = new NotificationThread(this);
|
||||
notificationThread.start();
|
||||
// Hide the window title.
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(layoutID);
|
||||
@@ -115,120 +116,65 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
/**
|
||||
* Configure toolbar of app
|
||||
*/
|
||||
protected final void initToolbar(final int toolbarId, final @NonNull String title) {
|
||||
Log.d(TAG, "initToolbar");
|
||||
final Toolbar toolbar = (Toolbar) findViewById(toolbarId);
|
||||
mColor = (int) getIntent().getLongExtra(MainActivity.PREF_APP_BAR_COLOR, 0xffffff);
|
||||
toolbar.setBackgroundColor(mColor);
|
||||
protected final void initToolbar(Intent intent) {
|
||||
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setBackgroundColor((int) intent.getLongExtra(MainActivity.PREF_APP_BAR_COLOR, 0xffffff));
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(title);
|
||||
actionBar.setTitle(null);
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
|
||||
initManualInput();
|
||||
setupSecretClickHandler(toolbar);
|
||||
}
|
||||
|
||||
private void setupSecretClickHandler(final @NonNull Toolbar toolbar) {
|
||||
private void initManualInput() {
|
||||
EditText manualInput = (EditText) findViewById(R.id.manual_input);
|
||||
|
||||
|
||||
manualInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
handleBarcode(v.getText().toString());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void setupSecretClickHandler(final @NonNull View toolbar) {
|
||||
// Configure increment handler
|
||||
counterThread = new DecrementCounterThread(counter, 700L);
|
||||
toolbar.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
String toastMessage = null;
|
||||
switch (counter.incrementAndGet()) {
|
||||
case 1:
|
||||
if (false == counterThread.isRunning()) {
|
||||
counterThread.start();
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
toastMessage = CLICK_MESSAGES[0];
|
||||
break;
|
||||
case 11:
|
||||
toastMessage = CLICK_MESSAGES[1];
|
||||
break;
|
||||
case 17:
|
||||
toastMessage = CLICK_MESSAGES[2];
|
||||
break;
|
||||
case 23:
|
||||
toastMessage = null;
|
||||
counterThread.pause();
|
||||
Toast.makeText(ctx, CLICK_MESSAGES[3], Toast.LENGTH_SHORT).show();
|
||||
if (counter == 15) {
|
||||
switchScanner();
|
||||
break;
|
||||
}
|
||||
Log.d(TAG, "toolbar clicked " + counter.get() + " times");
|
||||
|
||||
if (null != toastMessage) {
|
||||
notificationThread.addMessage(toastMessage);
|
||||
} else {
|
||||
counter++;
|
||||
}
|
||||
Log.d(TAG, "toolbar clicked " + counter + " times");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class DecrementCounterThread extends Thread {
|
||||
final long delay;
|
||||
final AtomicInteger counter;
|
||||
private boolean running = false;
|
||||
private int previous = 0;
|
||||
|
||||
public DecrementCounterThread(final AtomicInteger counter, final long delay) {
|
||||
// Configure decrement handler
|
||||
Log.d(TAG, "setupSecretClickHandler: DecrementCounterThread()");
|
||||
this.counter = counter;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
running = true;
|
||||
while (running) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
if (counter.get() > 0) {
|
||||
if (previous > counter.get()) {
|
||||
previous = counter.decrementAndGet();
|
||||
Log.d(TAG, "decrement counter, now " + counter.get());
|
||||
} else {
|
||||
previous = counter.get();
|
||||
}
|
||||
}
|
||||
} catch (final IllegalArgumentException | InterruptedException e) {
|
||||
Log.d(TAG, "disable counter decrease Thread", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
Log.d(TAG, "pause decrementer");
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void switchScanner() {
|
||||
final SharedPreferences prefs = getSharedPreferences("MainActivity", Context.MODE_PRIVATE);
|
||||
int idx = prefs.getInt(MainActivity.SCANNER_BACKEND_KEY, 0);
|
||||
|
||||
Log.d(TAG, "current scanner backend " + idx + ", " + MainActivity.SCANNER_BACKEND[idx].toString());
|
||||
if (idx >= MainActivity.SCANNER_BACKEND.length - 1) {
|
||||
idx = 0;
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
final SharedPreferences prefs = getSharedPreferences("scanner", Context.MODE_PRIVATE);
|
||||
|
||||
int idx = (prefs.getInt(MainActivity.SCANNER_BACKEND_KEY, 0) == MainActivity.ZXING)
|
||||
? MainActivity.ZBAR
|
||||
: MainActivity.ZXING;
|
||||
|
||||
Log.d(TAG, "switch to scanner backend " + idx + ", " + MainActivity.SCANNER_BACKEND[idx].toString());
|
||||
prefs.edit().putInt(MainActivity.SCANNER_BACKEND_KEY, idx).apply();
|
||||
|
||||
final Intent response = new Intent();
|
||||
response.putExtra("item", "restartScanner");
|
||||
cancelRequest("Scanner backend changed");
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -244,51 +190,60 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
|
||||
protected abstract View initScanner();
|
||||
|
||||
/**
|
||||
* Initialize network client
|
||||
*/
|
||||
protected final void initNetwork(final Intent intent) {
|
||||
Log.d(TAG, "initNetwork");
|
||||
final String url = intent.getStringExtra(MainActivity.PREF_API_URL);
|
||||
final String appToken = intent.getStringExtra(MainActivity.PREF_APP_TOKEN);
|
||||
final String token = intent.getStringExtra(MainActivity.PREF_POS_TOKEN);
|
||||
|
||||
Log.d(TAG, "initializing addScanner activity with url "
|
||||
+ url + ", appToken " + appToken + ", token " + token);
|
||||
|
||||
apiClient = new ApiClient(url, appToken, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles barcode.
|
||||
* Makes network call in separate thread and call
|
||||
* networkResponseCallback
|
||||
*
|
||||
* @param card scanned card number
|
||||
*/
|
||||
public void handleBarcode(final @NonNull String card) {
|
||||
Log.d(TAG, "handleBarcode");
|
||||
notificationThread.addMessage(card);
|
||||
networkThread = new NetworkThread(this, apiClient);
|
||||
networkThread.card(card).start();
|
||||
mClient.findUser(card, new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
handleFail(card);
|
||||
}
|
||||
|
||||
protected final void networkResponseCallback(final @NonNull Pair<String, String> result) {
|
||||
Log.d(TAG, "networkResponseCallback");
|
||||
if (null != result.first) {
|
||||
Log.d(TAG, "user found, finish activity with result");
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("user", result.second);
|
||||
intent.putExtra("card", result.first);
|
||||
setResult(RESULT_OK, intent);
|
||||
networkThread.cancel();
|
||||
notificationThread.cancel();
|
||||
finish();
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
try {
|
||||
ResponseBody body = response.body();
|
||||
if (body != null) {
|
||||
switch (response.code()) {
|
||||
case 200:
|
||||
final JSONArray users = new JSONArray(body.string());
|
||||
if (users.length() > 0) {
|
||||
handleSuccess(card, users.get(0).toString());
|
||||
} else {
|
||||
if (null != result.second) {
|
||||
notificationThread.addMessage(result.second);
|
||||
handleFail(card);
|
||||
}
|
||||
break;
|
||||
case 204:
|
||||
handleFail(card);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (final IOException | JSONException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
handleFail(card);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected final void handleSuccess(final String card, final String user) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setResult(RESULT_OK, new Intent().putExtra("user", user).putExtra("card", card));
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected final void handleFail(final String card) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String message = String.format(getString(R.string.identifier_not_found), card)
|
||||
+ ".\n"
|
||||
+ String.format(getString(R.string.error_contact_support), BuildConfig.supportPhone);
|
||||
Toast.makeText(AbstractScannerActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -297,10 +252,25 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
menu.findItem(R.id.settings).setIcon(getResources().getDrawable(R.drawable.settings));
|
||||
menu.findItem(R.id.faq).setIcon(getResources().getDrawable(R.drawable.help));
|
||||
menu.findItem(R.id.logout).setIcon(getResources().getDrawable(R.drawable.logout));
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.logout) {
|
||||
logoutDialog = LogoutDialogFragment.newInstance(mColor);
|
||||
if (item.getItemId() == R.id.settings) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra("item", "settings");
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.logout) {
|
||||
int color = (int) getIntent().getLongExtra(MainActivity.PREF_APP_BAR_COLOR, 0xffffff);
|
||||
logoutDialog = LogoutDialogFragment.newInstance(color);
|
||||
logoutDialog.show(getFragmentManager(), "logout");
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.faq) {
|
||||
@@ -366,54 +336,4 @@ public abstract class AbstractScannerActivity extends AppCompatActivity {
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
|
||||
private class NotificationThread extends Thread {
|
||||
|
||||
private final static int MAX_NOTIFICATION_MESSAGES = 2;
|
||||
|
||||
private boolean run = true;
|
||||
private final Queue<String> queue;
|
||||
private final AbstractScannerActivity ctx;
|
||||
|
||||
public NotificationThread(final @NonNull AbstractScannerActivity ctx) {
|
||||
this.queue = new ArrayBlockingQueue<>(MAX_NOTIFICATION_MESSAGES);
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (run) {
|
||||
if (null != queue.peek()) {
|
||||
Log.d(TAG, "null != queue.peek()");
|
||||
final String message = queue.poll();
|
||||
Log.d(TAG, "message: " + message);
|
||||
if (null != message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (final InterruptedException ie) {
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addMessage(final @NonNull String message) {
|
||||
if (queue.size() == MAX_NOTIFICATION_MESSAGES) {
|
||||
Log.d(TAG, "Discard message: " + queue.poll());
|
||||
}
|
||||
queue.add(message);
|
||||
Log.d(TAG, "Add message: " + message);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
package com.dinect.checker;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dinect.checker.net.ApiClient;
|
||||
import com.dinect.checker.zbar.CameraActivity;
|
||||
import com.dinect.checker.zxing.ScannerActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import io.flutter.app.FlutterActivity;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.lang.System;
|
||||
import java.util.Set;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
|
||||
@@ -30,112 +33,109 @@ public class MainActivity extends FlutterActivity {
|
||||
|
||||
private static final int START_SCANNER_REQUEST_CODE = 2017;
|
||||
|
||||
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";
|
||||
static final String PREF_API_URL = "prefs_api_token";
|
||||
static final String PREF_APP_TOKEN = "pres_app_token";
|
||||
static final String PREF_POS_TOKEN = "pref_pos_token";
|
||||
public static final String PREF_API_URL = "prefs_api_token";
|
||||
public static final String PREF_APP_TOKEN = "pres_app_token";
|
||||
public static final String PREF_POS_TOKEN = "pref_pos_token";
|
||||
static final String PREF_APP_BAR_COLOR = "pref_app_bar_color";
|
||||
|
||||
public static final int ZXING = 0;
|
||||
public static final int ZBAR = 1;
|
||||
|
||||
static final Class[] SCANNER_BACKEND = {
|
||||
ScannerActivity.class,
|
||||
CameraActivity.class,
|
||||
};
|
||||
|
||||
static final String SCANNER_BACKEND_KEY = "scanner_backend_idx";
|
||||
|
||||
private MethodChannel mChannel;
|
||||
private SharedPreferences mPreferences;
|
||||
private Map mScannerArgs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
GeneratedPluginRegistrant.registerWith(this);
|
||||
initLocale(this);
|
||||
mPreferences = getPreferences(Context.MODE_PRIVATE);
|
||||
|
||||
Log.d(TAG, "application prefs:");
|
||||
for(final Map.Entry<String, ?> kv: mPreferences.getAll().entrySet()){
|
||||
Log.d(TAG, " key = " + kv.getKey() + ", value = " + kv.getValue().toString());
|
||||
}
|
||||
|
||||
mChannel = new MethodChannel(getFlutterView(), "com.dinect.checker/instance_id");
|
||||
mChannel.setMethodCallHandler(
|
||||
new MethodCallHandler() {
|
||||
@Override
|
||||
public void onMethodCall(MethodCall call, Result result) {
|
||||
callMethod(call, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void callMethod(MethodCall call, Result result) {
|
||||
switch (call.method) {
|
||||
case "saveToken":
|
||||
Map tokenArguments = call.arguments();
|
||||
mPreferences.edit().putString(PREF_POS_TOKEN, (String) tokenArguments.get("token")).apply();
|
||||
break;
|
||||
case "getToken":
|
||||
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 "getLocale":
|
||||
result.success(BuildConfig.locale);
|
||||
result.success(getLanguage());
|
||||
break;
|
||||
case "getFlavor":
|
||||
result.success(BuildConfig.flavor);
|
||||
break;
|
||||
case "getMerchantID":
|
||||
result.success(mPreferences.getString(PREF_POS_MERCHANT_ID, null));
|
||||
case "getCurrency":
|
||||
result.success(BuildConfig.currency);
|
||||
break;
|
||||
case "startScanner":
|
||||
final Map arguments = call.arguments();
|
||||
final int idx = mPreferences.getInt(SCANNER_BACKEND_KEY, 0);
|
||||
Log.d(TAG, "use " + SCANNER_BACKEND[idx].toString() + " backend, with idx = " + idx);
|
||||
Intent cameraIntent = new Intent(MainActivity.this, SCANNER_BACKEND[idx]);
|
||||
cameraIntent.putExtra(PREF_API_URL, (String) arguments.get("url"));
|
||||
cameraIntent.putExtra(PREF_APP_TOKEN, (String) arguments.get("appToken"));
|
||||
cameraIntent.putExtra(PREF_POS_TOKEN, (String) arguments.get("token"));
|
||||
cameraIntent.putExtra(PREF_APP_BAR_COLOR, (Long) arguments.get("color"));
|
||||
startActivityForResult(cameraIntent, START_SCANNER_REQUEST_CODE);
|
||||
break;
|
||||
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));
|
||||
mScannerArgs = call.arguments();
|
||||
startScannerActivity();
|
||||
break;
|
||||
case "isOnline":
|
||||
boolean online = Utils.isOnline(MainActivity.this);
|
||||
if (!online) {
|
||||
Toast.makeText(MainActivity.this, "Проверьте интернет соединение", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
result.success(online);
|
||||
checkInternetConnection(result);
|
||||
break;
|
||||
case "getPosID":
|
||||
String posId = mPreferences.getString(PREF_POS_ID, null);
|
||||
if (posId == null) {
|
||||
posId = String.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
mPreferences.edit().putString(PREF_POS_ID, posId).apply();
|
||||
result.success(posId);
|
||||
case "getSupportPhone":
|
||||
result.success(BuildConfig.supportPhone);
|
||||
break;
|
||||
case "getSupportUrl":
|
||||
result.success(BuildConfig.supportUrl);
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private String getLanguage() {
|
||||
List<String> availableLanguages = Arrays.asList("ru", "en");
|
||||
if (availableLanguages.contains(Locale.getDefault().getLanguage())) {
|
||||
return Locale.getDefault().getLanguage();
|
||||
} else {
|
||||
return BuildConfig.locale;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkInternetConnection(Result result) {
|
||||
boolean connected = Utils.isOnline(this);
|
||||
if (!connected)
|
||||
Toast.makeText(this, "Проверьте интернет соединение", Toast.LENGTH_SHORT).show();
|
||||
result.success(connected);
|
||||
}
|
||||
|
||||
private void setLocale(String locale) {
|
||||
Resources res = getResources();
|
||||
Configuration configuration = new Configuration(res.getConfiguration());
|
||||
configuration.locale = new Locale(locale);
|
||||
Locale.setDefault(configuration.locale);
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
}
|
||||
|
||||
private void startScannerActivity() {
|
||||
final int idx = getSharedPreferences("scanner", Context.MODE_PRIVATE).getInt(SCANNER_BACKEND_KEY, 0);
|
||||
Intent cameraIntent = new Intent(MainActivity.this, SCANNER_BACKEND[idx]);
|
||||
cameraIntent.putExtra(PREF_API_URL, (String) mScannerArgs.get("url"));
|
||||
cameraIntent.putExtra(PREF_APP_TOKEN, (String) mScannerArgs.get("appToken"));
|
||||
cameraIntent.putExtra(PREF_POS_TOKEN, (String) mScannerArgs.get("token"));
|
||||
cameraIntent.putExtra(PREF_APP_BAR_COLOR, (Long) mScannerArgs.get("color"));
|
||||
setLocale((String) mScannerArgs.get("locale"));
|
||||
startActivityForResult(cameraIntent, START_SCANNER_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == START_SCANNER_REQUEST_CODE && resultCode == RESULT_CANCELED) {
|
||||
if (requestCode == START_SCANNER_REQUEST_CODE) {
|
||||
if (resultCode == RESULT_CANCELED) {
|
||||
finish();
|
||||
} else if (requestCode == START_SCANNER_REQUEST_CODE && resultCode == RESULT_OK) {
|
||||
} else if (resultCode == RESULT_OK) {
|
||||
if (data != null) {
|
||||
String user = data.getExtras().getString("user", null);
|
||||
if (user != null) {
|
||||
@@ -151,50 +151,26 @@ public class MainActivity extends FlutterActivity {
|
||||
mChannel.invokeMethod(menuItem, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
startScannerActivity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void initLocale(Context context) {
|
||||
Resources res = context.getResources();
|
||||
Configuration configuration = new Configuration(res.getConfiguration());
|
||||
switch (BuildConfig.locale) {
|
||||
case "en":
|
||||
configuration.locale = new Locale("en");
|
||||
Locale.setDefault(configuration.locale);
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
break;
|
||||
case "ru":
|
||||
configuration.locale = new Locale("ru");
|
||||
Locale.setDefault(configuration.locale);
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
break;
|
||||
case "ua":
|
||||
configuration.locale = new Locale("ua");
|
||||
Locale.setDefault(configuration.locale);
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void getFlavor() {
|
||||
|
||||
}
|
||||
|
||||
public void getCurrency() {
|
||||
|
||||
}
|
||||
|
||||
public void getLocale() {
|
||||
|
||||
}
|
||||
|
||||
public void handleItemClick() {
|
||||
|
||||
}
|
||||
|
||||
public void getDocID() {
|
||||
|
||||
}
|
||||
|
||||
public void removeKeys() {
|
||||
public void setUserLocale() {
|
||||
|
||||
}
|
||||
|
||||
@@ -202,32 +178,16 @@ public class MainActivity extends FlutterActivity {
|
||||
|
||||
}
|
||||
|
||||
public void getInstanceID() {
|
||||
|
||||
}
|
||||
|
||||
public void saveToken() {
|
||||
|
||||
}
|
||||
|
||||
public void getToken() {
|
||||
|
||||
}
|
||||
|
||||
public void getPosID() {
|
||||
|
||||
}
|
||||
|
||||
public void saveMerchantID() {
|
||||
|
||||
}
|
||||
|
||||
public void getMerchantID() {
|
||||
|
||||
}
|
||||
|
||||
public void isOnline() {
|
||||
|
||||
}
|
||||
|
||||
public void getSupportPhone() {
|
||||
|
||||
}
|
||||
|
||||
public void getSupportUrl() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 .
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dinect.checker;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.dinect.checker.net.ApiClient;
|
||||
|
||||
/**
|
||||
* Created by anonymous on 03.08.17.
|
||||
*/
|
||||
|
||||
public final class NetworkThread extends Thread {
|
||||
|
||||
private static final String TAG = "Checker.NetworkThread";
|
||||
|
||||
private AbstractScannerActivity activity;
|
||||
private final ApiClient client;
|
||||
private String card;
|
||||
|
||||
/**
|
||||
* @param activity caller activity (with networkResponseCallback())
|
||||
* @param client ApiClient instance
|
||||
*/
|
||||
public NetworkThread(final @NonNull AbstractScannerActivity activity, final @NonNull ApiClient client) {
|
||||
this.activity = activity;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (null != activity) {
|
||||
final Pair<String, String> response = client.findUser(activity, card);
|
||||
Log.d(TAG, "network request done with result: " + response.first);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.networkResponseCallback(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set card for network call
|
||||
*
|
||||
* @parm card number to search
|
||||
*/
|
||||
public NetworkThread card(final @NonNull String card) {
|
||||
this.card = card;
|
||||
return this;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
activity = null;
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,11 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static boolean isOnline(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo netInfo = cm.getActiveNetworkInfo();
|
||||
NetworkInfo netInfo = getConnectivityManager(context).getActiveNetworkInfo();
|
||||
return netInfo != null && netInfo.isConnected();
|
||||
}
|
||||
|
||||
private static ConnectivityManager getConnectivityManager(Context context) {
|
||||
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
}
|
||||
}
|
||||
@@ -15,101 +15,44 @@
|
||||
*/
|
||||
package com.dinect.checker.net;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.lang.String;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
import com.dinect.checker.R;
|
||||
|
||||
/**
|
||||
* Created by anonymous
|
||||
*/
|
||||
|
||||
public final class ApiClient {
|
||||
|
||||
private static final String TAG = "Checker.ApiClient";
|
||||
|
||||
private static final int TIMEOUT_CONNECTION = 3;
|
||||
private static final int TIMEOUT_READ = 3;
|
||||
private static final int TIMEOUT_WRITE = 3;
|
||||
private static final int TIMEOUT = 3;
|
||||
|
||||
public final String endpoint;
|
||||
public final String appToken;
|
||||
public final String token;
|
||||
private OkHttpClient mHttp;
|
||||
private String mEndpoint;
|
||||
|
||||
|
||||
final OkHttpClient http;
|
||||
|
||||
/**
|
||||
* @param endpoint POS API endpoint
|
||||
* @param appToken Application Token
|
||||
* @param token POS token
|
||||
*/
|
||||
public ApiClient(final @NonNull String endpoint, final @NonNull String appToken, final @NonNull String token) {
|
||||
this.endpoint = endpoint;
|
||||
this.appToken = appToken;
|
||||
this.token = token;
|
||||
|
||||
http = new OkHttpClient().
|
||||
public ApiClient(final String url, final @NonNull String appToken, final @NonNull String token) {
|
||||
mEndpoint = url;
|
||||
mHttp = new OkHttpClient().
|
||||
newBuilder()
|
||||
.connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)
|
||||
.readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
|
||||
.writeTimeout(TIMEOUT_WRITE, TimeUnit.SECONDS)
|
||||
.connectTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
.readTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
.addInterceptor(new DinectAuthorizationInterceptor(appToken, token, "checker/0.1", true))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
*
|
||||
* @param card card/foreigncarf number
|
||||
* @return (null, error) on fail or (card, user) info on success
|
||||
*/
|
||||
public Pair<String, String> findUser(Context ctx, final @NonNull String card) {
|
||||
|
||||
final Request.Builder builder = new Request.Builder();
|
||||
final Request request;
|
||||
|
||||
final HttpUrl.Builder httpBuilder = HttpUrl.parse(endpoint + "/users/").newBuilder();
|
||||
|
||||
httpBuilder.addQueryParameter("auto", card);
|
||||
request = builder
|
||||
.url(httpBuilder.build())
|
||||
.build();
|
||||
|
||||
try {
|
||||
final Response response = http.newCall(request).execute();
|
||||
final String body = response.body().string();
|
||||
final String NOT_FOUND_MESSAGE = String.format(ctx.getString(R.string.identifier_not_found, card));
|
||||
switch (response.code()) {
|
||||
case 200:
|
||||
final JSONArray users = new JSONArray(body);
|
||||
if (users.length() > 0) {
|
||||
return new Pair<>(card, users.get(0).toString());
|
||||
} else {
|
||||
return new Pair<>(null, NOT_FOUND_MESSAGE);
|
||||
}
|
||||
case 204:
|
||||
return new Pair<>(null, NOT_FOUND_MESSAGE);
|
||||
default:
|
||||
return new Pair<>(null, "Что-то пошло не так");
|
||||
}
|
||||
} catch (final IOException | JSONException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
return new Pair<>(null, "Упс...");
|
||||
public void findUser(String card, Callback callback) {
|
||||
final Request.Builder requestBuilder = new Request.Builder();
|
||||
final HttpUrl url = HttpUrl.parse(mEndpoint);
|
||||
if (url != null) {
|
||||
HttpUrl.Builder httpBuilder = url.newBuilder().addQueryParameter("auto", card);
|
||||
mHttp.newCall(requestBuilder.url(httpBuilder.build()).build()).enqueue(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,6 @@ public final class DinectAuthorizationInterceptor implements Interceptor {
|
||||
}
|
||||
|
||||
final Request request = requestBuilder.url(url).headers(headers).build();
|
||||
final Response response = chain.proceed(request);
|
||||
|
||||
return response;
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.dinect.checker.zbar;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dinect.checker.AbstractScannerActivity;
|
||||
@@ -19,7 +19,8 @@ import net.sourceforge.zbar.ImageScanner;
|
||||
import net.sourceforge.zbar.Symbol;
|
||||
import net.sourceforge.zbar.SymbolSet;
|
||||
|
||||
public class CameraActivity extends AbstractScannerActivity implements Camera.PreviewCallback {
|
||||
public class CameraActivity extends AbstractScannerActivity implements
|
||||
Camera.PreviewCallback {
|
||||
|
||||
public static final String ERROR_INFO = "ERROR_INFO";
|
||||
|
||||
@@ -39,12 +40,11 @@ public class CameraActivity extends AbstractScannerActivity implements Camera.Pr
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!init(R.layout.activity_zbar_scanner)) {
|
||||
if (!init(R.layout.a_zbar)) {
|
||||
return;
|
||||
}
|
||||
|
||||
initNetwork(getIntent());
|
||||
initToolbar(R.id.toolbar, getString(R.string.scanner_title));
|
||||
initToolbar(getIntent());
|
||||
|
||||
mPreview = (CameraPreview) initScanner();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.dinect.checker.AbstractScannerActivity;
|
||||
import com.dinect.checker.R;
|
||||
@@ -35,23 +36,21 @@ import me.dm7.barcodescanner.zxing.ZXingScannerView;
|
||||
public class ScannerActivity extends AbstractScannerActivity
|
||||
implements ZXingScannerView.ResultHandler {
|
||||
|
||||
private static final int SCAN_INTERVAL_PERIOD = 2000;
|
||||
private static final int SCAN_INTERVAL_PERIOD = 500;
|
||||
|
||||
private ZXingScannerView scannerView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
if (!init(R.layout.activity_zxing_scanner)) {
|
||||
|
||||
if (!init(R.layout.a_zxing)) {
|
||||
return;
|
||||
}
|
||||
initNetwork(getIntent());
|
||||
initToolbar(R.id.zxingToolbar, getString(R.string.scanner_title));
|
||||
|
||||
initToolbar(getIntent());
|
||||
|
||||
scannerView = (ZXingScannerView) initScanner();
|
||||
ArrayList<BarcodeFormat> formats = new ArrayList<>();
|
||||
formats.add(BarcodeFormat.UPC_EAN_EXTENSION);
|
||||
scannerView.setFormats(formats);
|
||||
addScanner(scannerView, R.id.zxingRoot);
|
||||
}
|
||||
|
||||
@@ -81,12 +80,8 @@ public class ScannerActivity extends AbstractScannerActivity
|
||||
|
||||
@Override
|
||||
public void handleResult(Result raw) {
|
||||
final String card = raw.getText();
|
||||
|
||||
handleBarcode(card);
|
||||
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
handleBarcode(raw.getText());
|
||||
scannerView.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scannerView.resumeCameraPreview(ScannerActivity.this);
|
||||
|
||||
BIN
android/app/src/main/res/drawable-xxxhdpi/help.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_more_vert.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/settings.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
@@ -41,11 +41,7 @@
|
||||
android:layout_marginTop="56dp"
|
||||
android:background="#00ff00" />
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:titleTextColor="@android:color/white" />
|
||||
<include layout="@layout/v_custom_toolbar" />
|
||||
|
||||
<View
|
||||
android:id="@+id/toolbarShadow"
|
||||
@@ -6,11 +6,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/zxingToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:titleTextColor="@android:color/white" />
|
||||
<include layout="@layout/v_custom_toolbar" />
|
||||
|
||||
<View
|
||||
android:id="@+id/zxingToolbarShadow"
|
||||
21
android/app/src/main/res/layout/v_custom_toolbar.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:titleTextColor="@android:color/white">
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:id="@+id/manual_input"
|
||||
android:hint="@string/enter_manual"
|
||||
android:maxLines="1"
|
||||
android:inputType="text"
|
||||
android:imeOptions="actionDone"
|
||||
android:layout_marginLeft="72dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textColorHint="@android:color/white"/>
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
@@ -2,16 +2,27 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_more_vert"
|
||||
app:showAsAction="ifRoom">
|
||||
|
||||
<menu>
|
||||
|
||||
<group android:id="@+id/items"/>
|
||||
|
||||
<item android:id="@+id/settings"
|
||||
android:title="@string/settings"
|
||||
android:icon="@drawable/settings"/>
|
||||
|
||||
<item android:id="@+id/faq"
|
||||
android:orderInCategory="0"
|
||||
android:title="@string/faq"
|
||||
android:icon="@drawable/help_outline"
|
||||
app:showAsAction="ifRoom"/>
|
||||
android:icon="@drawable/help"/>
|
||||
|
||||
<item android:id="@+id/logout"
|
||||
android:orderInCategory="1"
|
||||
android:title="@string/logout"
|
||||
android:icon="@drawable/logout"
|
||||
app:showAsAction="ifRoom"/>
|
||||
android:icon="@drawable/logout"/>
|
||||
|
||||
</menu>
|
||||
</item>
|
||||
</menu>
|
||||
@@ -2,11 +2,14 @@
|
||||
<string name="app_name">AutoBonus</string>
|
||||
<string name="scanner_title">Сканер карты</string>
|
||||
<string name="scan">Сканировать</string>
|
||||
<string name="faq">FAQ</string>
|
||||
<string name="faq">Справка</string>
|
||||
<string name="logout">Выход</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="logout_title">Подтверждение</string>
|
||||
<string name="logout_text">Вы действительно хотите выйти и ввести другой номер магазина?</string>
|
||||
<string name="logout_yes">Да</string>
|
||||
<string name="logout_no">Нет</string>
|
||||
<string name="identifier_not_found">"Идентификатор %s не найден"</string>
|
||||
<string name="enter_manual">Введите штрихкод вручную</string>
|
||||
<string name="error_contact_support">Можете воспользоваться ручным вводом или позвонить на номер:%s</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
<string name="app_name">AutoBonus</string>
|
||||
<string name="scanner_title">Сканер карти</string>
|
||||
<string name="scan">Сканувати</string>
|
||||
<string name="faq">FAQ</string>
|
||||
<string name="faq">Допомога</string>
|
||||
<string name="logout">Вихід</string>
|
||||
<string name="settings">Налаштування</string>
|
||||
<string name="logout_title">Підтвердження</string>
|
||||
<string name="logout_text">Ви дійсно хочете вийти і ввести інший номер магазину?</string>
|
||||
<string name="logout_yes">Так</string>
|
||||
<string name="logout_no">Ні</string>
|
||||
<string name="identifier_not_found">"Ідентифікатор %s не знайден"</string>
|
||||
<string name="enter_manual">Введіть штрихкод вручну</string>
|
||||
<string name="error_contact_support">Можете скористатися ручним введенням або зателефонувати на номер:\n%s</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
<string name="app_name">AutoBonus</string>
|
||||
<string name="scanner_title">Card Scanner</string>
|
||||
<string name="scan">Scan</string>
|
||||
<string name="faq">FAQ</string>
|
||||
<string name="faq">Help</string>
|
||||
<string name="logout">Logout</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="logout_title">Сonfirmation</string>
|
||||
<string name="logout_text">Do you really want to log out and enter a different store number?</string>
|
||||
<string name="logout_yes">Yes</string>
|
||||
<string name="logout_no">No</string>
|
||||
<string name="identifier_not_found">"Identifier %s is not found"</string>
|
||||
<string name="enter_manual">Enter the barcode manually</string>
|
||||
<string name="error_contact_support">You can use manual input or call the number:\n%s</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar"/>
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:textColorSecondary">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.dinect.checker"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0.1">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="21"/>
|
||||
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".zbar.CameraActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme"/>
|
||||
|
||||
<activity
|
||||
android:name=".zxing.ScannerActivity"
|
||||
android:theme="@style/AppTheme"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
Before Width: | Height: | Size: 17 KiB |
@@ -4,7 +4,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
assets/check.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 173 B After Width: | Height: | Size: 173 B |
BIN
assets/help.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
assets/settings.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/settings_arrow.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
16
checker.iml
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="checker" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="FLUTTER_MODULE_TYPE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
33
ios/Podfile.lock
Normal file
@@ -0,0 +1,33 @@
|
||||
PODS:
|
||||
- Flutter (1.0.0)
|
||||
- FMDB (2.7.2):
|
||||
- FMDB/standard (= 2.7.2)
|
||||
- FMDB/standard (2.7.2)
|
||||
- path_provider (0.0.1):
|
||||
- Flutter
|
||||
- sqflite (0.0.1):
|
||||
- Flutter
|
||||
- FMDB
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `/Users/kifio/flutter/bin/cache/artifacts/engine/ios`)
|
||||
- path_provider (from `/Users/kifio/.pub-cache/hosted/pub.dartlang.org/path_provider-0.2.1+1/ios`)
|
||||
- sqflite (from `/Users/kifio/.pub-cache/hosted/pub.dartlang.org/sqflite-0.2.2/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: /Users/kifio/flutter/bin/cache/artifacts/engine/ios
|
||||
path_provider:
|
||||
:path: /Users/kifio/.pub-cache/hosted/pub.dartlang.org/path_provider-0.2.1+1/ios
|
||||
sqflite:
|
||||
:path: /Users/kifio/.pub-cache/hosted/pub.dartlang.org/sqflite-0.2.2/ios
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: d674e78c937094a75ac71dd77e921e840bea3dbf
|
||||
FMDB: 6198a90e7b6900cfc046e6bc0ef6ebb7be9236aa
|
||||
path_provider: f96fff6166a8867510d2c25fdcc346327cc4b259
|
||||
sqflite: 8e2d9fe1e7cdc95d4d537fc7eb2d23c8dc428e3c
|
||||
|
||||
PODFILE CHECKSUM: 351e02e34b831289961ec3558a535cbd2c4965d2
|
||||
|
||||
COCOAPODS: 1.2.0
|
||||
@@ -11,6 +11,7 @@
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
|
||||
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
755861CA44FB15BD2EFD12F7 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28B464359F9DDCC3EF756D7D /* libPods-Runner.a */; };
|
||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
|
||||
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
|
||||
@@ -54,6 +55,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
28B464359F9DDCC3EF756D7D /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
@@ -120,12 +122,20 @@
|
||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
|
||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
|
||||
BBA9BB5B1F179C320053B6EA /* libzbar.a in Frameworks */,
|
||||
755861CA44FB15BD2EFD12F7 /* libPods-Runner.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
578B8FA7D56ACA2E56C02128 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -148,6 +158,7 @@
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
BBA9BB001F1786510053B6EA /* Frameworks */,
|
||||
578B8FA7D56ACA2E56C02128 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -195,6 +206,7 @@
|
||||
BBA9BB351F1792690053B6EA /* CoreMedia.framework */,
|
||||
BBA9BB331F17925F0053B6EA /* CoreGraphics.framework */,
|
||||
BBA9BB311F1792570053B6EA /* AVFoundation.framework */,
|
||||
28B464359F9DDCC3EF756D7D /* libPods-Runner.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -270,12 +282,15 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
586CFDEBE2C82C2A9F7DA22E /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
889CEA9B47E2F8AFF76F6433 /* [CP] Embed Pods Frameworks */,
|
||||
1F7C9AAD1A9D38F5484B84D1 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -342,6 +357,21 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
1F7C9AAD1A9D38F5484B84D1 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -356,6 +386,36 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
|
||||
};
|
||||
586CFDEBE2C82C2A9F7DA22E /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
889CEA9B47E2F8AFF76F6433 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
||||
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
@@ -4,4 +4,7 @@
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
@@ -17,8 +17,6 @@ extension ZBarSymbolSet: Sequence {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Реализовать окно сканнера в этом контроллере, вместо вызова ZBarReaderViewController
|
||||
|
||||
@objc class ScannerViewController: UIViewController, ZBarReaderDelegate {
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
13
lib/base/base_screen.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class BaseScreen extends StatefulWidget {
|
||||
|
||||
final SqliteHelper helper;
|
||||
final String app;
|
||||
|
||||
BaseScreen(this.helper, this.app);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState();
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:checker/resources.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'common.dart';
|
||||
import 'consts.dart';
|
||||
import 'strings.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/screens/settings.dart';
|
||||
import 'package:checker/screens/faq.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:checker/db.dart';
|
||||
|
||||
abstract class BaseState<T extends StatefulWidget> extends State<T> {
|
||||
|
||||
/// Класс для работы с бд.
|
||||
SqliteHelper helper;
|
||||
|
||||
/// Тип сборки. Определяет, какие брать ресурсы (цвета, картинки)
|
||||
String app;
|
||||
|
||||
@@ -21,24 +24,7 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
|
||||
String error;
|
||||
|
||||
/// Введенное пользователем значение.
|
||||
String textFieldValue = '';
|
||||
|
||||
@override Widget build(BuildContext ctx) {
|
||||
|
||||
platform.invokeMethod('getLocale').then((locale) {
|
||||
Intl.defaultLocale = locale;
|
||||
if (app == null) {
|
||||
platform.invokeMethod('getFlavor').then((flavor) {
|
||||
setState(() {
|
||||
app = flavor;
|
||||
onStart();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return getMainWidget();
|
||||
}
|
||||
String dinCode = '';
|
||||
|
||||
Widget getMainWidget() {
|
||||
return app == null ? getBackground() : new Scaffold(appBar: getAppBar(),
|
||||
@@ -56,31 +42,63 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
|
||||
fit: BoxFit.cover)));
|
||||
}
|
||||
|
||||
void onStart() {
|
||||
|
||||
}
|
||||
|
||||
/// Возвращает контейнер с всеми виджетами экрана.
|
||||
Widget getScreenContent();
|
||||
|
||||
/// Возвращает заголовок для AppBar
|
||||
String getTitle();
|
||||
String getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
AppBar getAppBar() {
|
||||
return new AppBar(title: new Text(getTitle(), style: new TextStyle(fontSize: 18.0)),
|
||||
return new AppBar(title: new Container(
|
||||
margin: new EdgeInsets.only(left: 16.0),
|
||||
child: new Text(getTitle(), style: new TextStyle(fontSize: 18.0))),
|
||||
backgroundColor: Resources.getPrimaryColor(app), actions: getMenuButtons());
|
||||
}
|
||||
|
||||
List<Widget> getMenuButtons() {
|
||||
return <Widget>[getFaqButton()];
|
||||
return <Widget>[
|
||||
new PopupMenuButton<int>(
|
||||
onSelected: onOptionsItemClick,
|
||||
itemBuilder: (BuildContext context) {
|
||||
return [new PopupMenuItem(
|
||||
value: 0,
|
||||
child: getMenuItem(settings_png, StringsLocalization.settings())),
|
||||
new PopupMenuItem(
|
||||
value: 1,
|
||||
child: getMenuItem(help_png, StringsLocalization.help())),
|
||||
new PopupMenuItem(
|
||||
value: 2,
|
||||
child: getMenuItem(logout_png, StringsLocalization.logout()))
|
||||
];
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
Widget getFaqButton() {
|
||||
return new IconButton(icon: new Icon(Icons.help_outline), onPressed: () => faq(context, false));
|
||||
void onOptionsItemClick(int index) {
|
||||
switch (index) {
|
||||
case 0: {
|
||||
pushRoute(context, new SettingsScreen(helper, app, false));
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
pushRoute(context, new FAQScreen(false));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
logout(context, helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget getLogoutButton() {
|
||||
return new IconButton(icon: new Image.asset(logout_png, height: iconHeight, width: iconHeight), onPressed: () => logout(context));
|
||||
/// Возвращает пункт меню (Картинка с текстом)
|
||||
Widget getMenuItem(String image, String text) {
|
||||
return new Row(children: [
|
||||
new Image.asset(image, width: 28.0, height: 28.0),
|
||||
new Container(padding: new EdgeInsets.only(left: 8.0), child: new Text(text))
|
||||
]);
|
||||
}
|
||||
|
||||
/// Возврвщает контейнер, внутри которого Text с подсказкой.
|
||||
@@ -88,31 +106,31 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
|
||||
double horizontalMargin = 8.0;
|
||||
return new Container(margin: new EdgeInsets.only(top: horizontalMargin, bottom: horizontalMargin, left: verticalMargin, right: verticalMargin),
|
||||
child: new Row(crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[new Text(getHintString(), textAlign: TextAlign.left,
|
||||
style: new TextStyle(fontWeight: FontWeight.w300, color: error == null ? greyTextColor : Resources.getLogo(app), fontSize: 14.0))]));
|
||||
children: <Widget>[new Text(getHintOrError(), textAlign: TextAlign.left,
|
||||
style: new TextStyle(fontWeight: FontWeight.w300, color: error == null ? greyTextColor : Resources.getPrimaryColor(app), fontSize: 14.0))]));
|
||||
}
|
||||
|
||||
/// Возвращает подсказку, либо ошибку, если введенные в поле ввода данные неверны.
|
||||
String getHintString() {
|
||||
if (textFieldValue.length == 0 && error == null) {
|
||||
String getHintOrError() {
|
||||
if (dinCode.length == 0 && error == null) {
|
||||
return ' ';
|
||||
} else if (error != null) {
|
||||
return error;
|
||||
} else {
|
||||
return getHint();
|
||||
return getHintString();
|
||||
}
|
||||
}
|
||||
|
||||
/// Возвращает текст подсказки для поля ввода.
|
||||
/// Должен быть переопределен на экранах, на которых есть поле ввода.
|
||||
String getHint() {
|
||||
String getHintString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Смена состояния экрана при изменении текста в поле ввода.
|
||||
void handleUserInput(String text) {
|
||||
setState(() {
|
||||
textFieldValue = text;
|
||||
dinCode = text;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -185,5 +203,4 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
|
||||
Widget wrapButton(EdgeInsets margin, Widget widget) {
|
||||
return new Container(margin: margin, height: buttonHeight, child: new Row(children: <Widget>[new Expanded(child: widget)]));
|
||||
}
|
||||
|
||||
}
|
||||
68
lib/base/settings_base_state.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class SettingsBaseState<T extends StatefulWidget> extends BaseState<T> {
|
||||
|
||||
SettingsBaseState(SqliteHelper helper, String app) {
|
||||
this.helper = helper;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
int selectedItem;
|
||||
|
||||
@override Widget build(BuildContext context) {
|
||||
return new Scaffold(appBar: getAppBar(),
|
||||
body: getScreenContent());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getScreenContent() {
|
||||
getSelectedValue();
|
||||
List<Widget> widgets = new List();
|
||||
|
||||
for (String option in getOptions()) {
|
||||
widgets.add(getItem(option));
|
||||
}
|
||||
|
||||
return new ListView(children: widgets);
|
||||
}
|
||||
|
||||
List<String> getOptions();
|
||||
|
||||
void saveOption();
|
||||
|
||||
void getSelectedValue();
|
||||
|
||||
@override
|
||||
List<Widget> getMenuButtons() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget getItem(String option) {
|
||||
return new Container(
|
||||
height: 56.0,
|
||||
child: (new FlatButton(onPressed: () {
|
||||
saveOption();
|
||||
setState(() {
|
||||
selectedItem = getOptions().indexOf(option);
|
||||
});
|
||||
},
|
||||
child: new Row(children: <Widget>[
|
||||
new Expanded(child: new Text(option)),
|
||||
getCheckMark(getOptions().indexOf(option))]))));
|
||||
}
|
||||
|
||||
Widget getCheckMark(int index) {
|
||||
return index == selectedItem ? new Image.asset(check_png,
|
||||
width: 28.0,
|
||||
height: 28.0) : new Image.asset(check_png, color: new Color(0xffffff));
|
||||
}
|
||||
|
||||
@override
|
||||
String getTitle() {
|
||||
return StringsLocalization.settings();
|
||||
}
|
||||
}
|
||||
120
lib/common.dart
@@ -1,40 +1,50 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:checker/screens/faq.dart';
|
||||
import 'package:checker/screens/purchase.dart';
|
||||
import 'package:checker/screens/settings.dart';
|
||||
import 'package:checker/screens/splash.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'consts.dart';
|
||||
import 'db.dart';
|
||||
import 'network.dart';
|
||||
import 'resources.dart';
|
||||
import 'package:checker/registration.dart';
|
||||
import 'package:checker/purchase.dart';
|
||||
import 'faq.dart';
|
||||
import 'strings.dart';
|
||||
|
||||
// Канал для взаимодействия с кодом платформы.
|
||||
const platform = const MethodChannel('com.dinect.checker/instance_id');
|
||||
|
||||
// Метод обеспечивает замену текущего объекта route новым.
|
||||
pushRoute(BuildContext context, Widget widget) {
|
||||
pushRouteReplacement(BuildContext context, Widget widget) {
|
||||
var route = new MaterialPageRoute<Null>(builder: (BuildContext context) => widget);
|
||||
Navigator.of(context).pushReplacement(route);
|
||||
}
|
||||
|
||||
// Добавление route, с возможностью вернуться к предыдущему экрану.
|
||||
faq(BuildContext context, bool returnToScanner) {
|
||||
var route = new MaterialPageRoute<Null>(builder: (BuildContext context) => new FAQScreen(returnToScanner));
|
||||
pushRoute(BuildContext context, Widget widget) {
|
||||
var route = new MaterialPageRoute<Null>(builder: (BuildContext context) => widget);
|
||||
Navigator.of(context).push(route);
|
||||
}
|
||||
|
||||
// Добавление route, с возможностью вернуться к предыдущему экрану.
|
||||
faq(BuildContext context, bool returnToScanner) {
|
||||
pushRoute(context, new FAQScreen(returnToScanner));
|
||||
}
|
||||
|
||||
// В методе отправляется запрос на удаление токена кассы, очищаются SharedPreferences приложения.
|
||||
logout(BuildContext context) async {
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
VoidCallback positiveCalback = () {
|
||||
logout(BuildContext context, SqliteHelper helper) async {
|
||||
|
||||
String token = await helper.getToken();
|
||||
String locale = await helper.getLocale();
|
||||
|
||||
VoidCallback positiveCallback = () {
|
||||
if (token != null) {
|
||||
deleteToken(token).then((response) {
|
||||
print(response.body);
|
||||
platform.invokeMethod('removeKeys').then((result) {
|
||||
deleteToken(token, locale).then((response) {
|
||||
helper.clear().then((result) {
|
||||
helper.close().then((_) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
pushRoute(context, new RegistrationScreen()); // Запускаем регистрацию
|
||||
pushRouteReplacement(context, new SplashScreen()); // Запускаем регистрацию
|
||||
});
|
||||
});
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
@@ -45,16 +55,22 @@ logout(BuildContext context) async {
|
||||
}
|
||||
};
|
||||
|
||||
showYesNoDialog(context, StringsLocalization.confirmation(), StringsLocalization.askChangeStore(), positiveCalback);
|
||||
showYesNoDialog(context, StringsLocalization.confirmation(), StringsLocalization.askChangeStore(), positiveCallback);
|
||||
}
|
||||
|
||||
forceLogout(BuildContext context) async {
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
deleteToken(token).then((response) {
|
||||
print(response.body);
|
||||
platform.invokeMethod('removeKeys').then((result) {
|
||||
forceLogout(String token , BuildContext context) async {
|
||||
|
||||
deleteToken(token, 'ru').then((response) {
|
||||
SqliteHelper helper = new SqliteHelper();
|
||||
helper.open().then((_) {
|
||||
helper.clear().then((_) {
|
||||
helper.close().then((_) {
|
||||
while (Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
pushRoute(context, new RegistrationScreen()); // Запускаем регистрацию
|
||||
}
|
||||
pushRouteReplacement(context, new SplashScreen());
|
||||
});
|
||||
});
|
||||
});
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
@@ -63,36 +79,55 @@ forceLogout(BuildContext context) async {
|
||||
|
||||
/// Запуск спецефичной для каждой платформы части приложения - сканера.
|
||||
/// Может производиться с нескольких экранов (splash, finish_registration).
|
||||
startScanner(BuildContext context, String app) async {
|
||||
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
startScanner(BuildContext context, String app, SqliteHelper helper) async {
|
||||
if (helper == null) {
|
||||
helper = new SqliteHelper();
|
||||
helper.open().then((_) {
|
||||
startScanner(context, app, helper);
|
||||
});
|
||||
} else {
|
||||
String token = await helper.getToken();
|
||||
String locale = await helper.getLocale();
|
||||
helper.close();
|
||||
// Канал ловит вызовы методов из "нативной" части приложения.
|
||||
// Могут быть вызваны либо logaut либо faq, либо purchase.
|
||||
// Могут быть вызваны либо logout либо faq, либо purchase.
|
||||
if (token != null) {
|
||||
platform.setMethodCallHandler((MethodCall call) async {
|
||||
|
||||
print('call.method: ${call.method}');
|
||||
if (call.method == 'logout') {
|
||||
forceLogout(context);
|
||||
forceLogout(token, context);
|
||||
} else if (call.method == 'faq') {
|
||||
faq(context, true);
|
||||
} else if(call.method == 'settings') {
|
||||
helper = new SqliteHelper();
|
||||
helper.open().then((_) {
|
||||
pushRoute(context, new SettingsScreen(helper, app, true));
|
||||
});
|
||||
} else {
|
||||
String userString = call.arguments[0];
|
||||
print('user: ${userString}');
|
||||
String card = call.arguments[1];
|
||||
print('card: ${card}');
|
||||
var route = new MaterialPageRoute<Null>(builder: (BuildContext context) => new PurchaseScreen(userString, card));
|
||||
var route = new MaterialPageRoute<Null>(
|
||||
builder: (BuildContext context) =>
|
||||
new PurchaseScreen(
|
||||
userString, card));
|
||||
while (Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
Navigator.of(context).pushReplacement(route);
|
||||
}
|
||||
});
|
||||
|
||||
await platform.invokeMethod('startScanner', {
|
||||
'token': token,
|
||||
'url': url,
|
||||
'appToken': appToken,
|
||||
'color': Resources.getPrimaryColor(app).value
|
||||
'locale': locale,
|
||||
'color': Resources
|
||||
.getPrimaryColor(app)
|
||||
.value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Запуск диалога с двумя кнопками
|
||||
showYesNoDialog(BuildContext context, String title, String content, VoidCallback positiveCallback) {
|
||||
@@ -110,3 +145,22 @@ showYesNoDialog(BuildContext context, String title, String content, VoidCallback
|
||||
child: new Text(StringsLocalization.yes()),
|
||||
onPressed: positiveCallback)]));
|
||||
}
|
||||
|
||||
getCurrencyTitle(int code) {
|
||||
switch(code) {
|
||||
case 643: return StringsLocalization.nominativeRuble();
|
||||
case 840: return StringsLocalization.nominativeDollar();
|
||||
case 980: return StringsLocalization.nominativeHryvna();
|
||||
case 978: return StringsLocalization.nominativeEuro();
|
||||
case 398: return StringsLocalization.nominativeTenge();
|
||||
}
|
||||
}
|
||||
|
||||
getLocaleTitle(String code) {
|
||||
switch(code) {
|
||||
case 'ru': return 'Русский';
|
||||
case 'en': return 'English';
|
||||
case 'ua': return 'Український';
|
||||
case 'es': return 'Español';
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Serious constants
|
||||
const String appName = "AutoBonus";
|
||||
const String appName = "Autobonus";
|
||||
|
||||
const String url = 'https://pos-api-autoclub.dinect.com/20130701/';
|
||||
const String appToken = 'bdea0f3ba9034b688019a7cac753d1209e2b227f';
|
||||
|
||||
//const String url = 'https://pos-api-int.dinect.com/20130701/';
|
||||
//const String appToken = '9fec83cdca38c357e6b65dbb17514cdd36bf2a08';
|
||||
|
||||
// Assets
|
||||
const String logout_png = 'assets/logout.png';
|
||||
const String help_png = 'assets/help.png';
|
||||
const String settings_png = 'assets/settings.png';
|
||||
const String settings_arrow_png = 'assets/settings_arrow.png';
|
||||
const String check_png = 'assets/check.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';
|
||||
const String expansion_icon_png = 'assets/expansion_icon.png';
|
||||
const String expansion_icon_png = 'assets/faq_expansion_icon.png';
|
||||
const String powered_by_dinect_splash_png = 'assets/powered_by_dinect_splash.png';
|
||||
const String powered_by_dinect_png = 'assets/powered_by_dinect.png';
|
||||
const String splash_text_png = 'assets/splash_text.png';
|
||||
|
||||
143
lib/db.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
/// Данные о таблице сессии пользователя.
|
||||
const String tableSession = "session";
|
||||
|
||||
const String columnMerchantID = "merchant_id"; // DIN code, который вводится при авторизации
|
||||
const String columnToken = "token"; // Токен для pos. Приходит с бэкэнда.
|
||||
const String columnPosID = "pos_id"; // идентификатор для создания токена на бэке.
|
||||
const String columnDocID = "doc_id"; // идентификатор, для проведения покупки на бэкенде.
|
||||
|
||||
/// Данные о таблице данных приложения.
|
||||
const String tableSettings = "settings";
|
||||
|
||||
const String columnCurrency = "currency"; // валюта.
|
||||
const String columnLocale = "locale"; // локаль.
|
||||
|
||||
//{
|
||||
// columnMerchantID: merchantID,
|
||||
// columnToken: token,
|
||||
// columnPosID: posID,
|
||||
// columnDocID: docID
|
||||
//}
|
||||
|
||||
/// База данных, для хранения временных данных (din, token, locale, etc.)
|
||||
class SqliteHelper {
|
||||
|
||||
Database db;
|
||||
|
||||
Future open() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, "demo.db");
|
||||
db = await openDatabase(path, version: 1,
|
||||
onCreate: (Database db, int version) async {
|
||||
|
||||
await db.execute('''create table $tableSession (
|
||||
$columnMerchantID text primary key,
|
||||
$columnToken text,
|
||||
$columnPosID text,
|
||||
$columnDocID integer)''');
|
||||
|
||||
await db.execute('''create table $tableSettings (
|
||||
$columnCurrency integer,
|
||||
$columnLocale text)''');
|
||||
});
|
||||
}
|
||||
|
||||
/// Создается запись в таблице, содержащая
|
||||
/// необходимые для идентификации пользователя и проведения запросов.
|
||||
Future createSession(String merchantID, String posID, String token) async {
|
||||
|
||||
Map session = {
|
||||
columnMerchantID: merchantID,
|
||||
columnPosID: posID,
|
||||
columnToken: token,
|
||||
columnDocID: 0
|
||||
};
|
||||
|
||||
return db.insert(tableSession, session);
|
||||
}
|
||||
|
||||
/// Создается запись в таблице, содержащая данные, которые не зависят от сессии.
|
||||
Future createAppInfo(String locale, int currency) async {
|
||||
List<Map> appInfo = await db.query(tableSettings);
|
||||
if (appInfo.length > 0) {
|
||||
return null;
|
||||
} else {
|
||||
return db.insert(tableSettings, {
|
||||
columnCurrency: currency
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map> getSettings() async {
|
||||
return await selectAll(tableSettings);
|
||||
}
|
||||
|
||||
Future<String> getToken() async {
|
||||
Map session = await selectAll(tableSession);
|
||||
String token = session != null ? session[columnToken] : null;
|
||||
return token;
|
||||
}
|
||||
|
||||
Future<String> getMerchantID() async {
|
||||
Map session = await selectAll(tableSession);
|
||||
String merchantID = session != null ? session[columnMerchantID] : null;
|
||||
return merchantID;
|
||||
}
|
||||
|
||||
Future<String> getPosID() async {
|
||||
Map session = await selectAll(tableSession);
|
||||
return session != null ? session[columnPosID] : new DateTime.now().millisecondsSinceEpoch.toString();
|
||||
}
|
||||
|
||||
Future<int> getDocID() async {
|
||||
Map session = await selectAll(tableSession);
|
||||
int docID = session != null ? session[columnDocID] : 0;
|
||||
db.update(tableSession, {columnDocID: docID + 1});
|
||||
return docID;
|
||||
}
|
||||
|
||||
Future<String> getLocale() async {
|
||||
Map settings = await selectAll(tableSettings);
|
||||
String locale = settings != null ? settings[columnLocale] : null;
|
||||
return locale;
|
||||
}
|
||||
|
||||
Future saveLocale(String locale) async {
|
||||
db.update(tableSettings, {columnLocale: locale});
|
||||
}
|
||||
|
||||
Future<int> getCurrency() async {
|
||||
Map settings = await selectAll(tableSettings);
|
||||
int currency = settings != null ? settings[columnCurrency] : null;
|
||||
return currency;
|
||||
}
|
||||
|
||||
Future saveCurrency(int currency) async {
|
||||
db.update(tableSettings, {columnCurrency: currency});
|
||||
}
|
||||
|
||||
Future<Map> selectAll(String table) async {
|
||||
|
||||
List<Map> maps = await db.query(table, columns: null);
|
||||
|
||||
if (maps.length > 0) {
|
||||
return maps.first;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future clear() async {
|
||||
return await db.delete(tableSession, where: null);
|
||||
}
|
||||
|
||||
Future close() async => db.close();
|
||||
|
||||
}
|
||||
131
lib/faq.dart
@@ -1,131 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'base_state.dart';
|
||||
import 'consts.dart';
|
||||
import 'common.dart';
|
||||
|
||||
/// Класс содержит заголовки и текст блоков FAQ.
|
||||
class Entry {
|
||||
Entry(this.title, this.text);
|
||||
final String title;
|
||||
final String text;
|
||||
}
|
||||
|
||||
class EntryItem extends StatelessWidget {
|
||||
|
||||
const EntryItem(this.entry);
|
||||
final Entry entry;
|
||||
|
||||
Widget _buildTiles(BuildContext context, Entry root) {
|
||||
EdgeInsets margin = new EdgeInsets.only(left: 20.0, right: 20.0);
|
||||
TextStyle titleStyle = Theme.of(context).textTheme.button.copyWith(fontWeight: FontWeight.bold, color: faqTitlesColor);
|
||||
return new Container(margin: margin, child: new Card(child: new ExpansionTile(
|
||||
key: new PageStorageKey<Entry>(root),
|
||||
title:new Text(root.title, style: titleStyle),
|
||||
children: [new Container(margin: margin, padding: new EdgeInsets.only(top: 12.0, bottom: 20.0),
|
||||
child: new Text(root.text, style: new TextStyle(fontWeight: FontWeight.w300, color: faqGrey, fontSize: 14.0)),
|
||||
decoration: new BoxDecoration(border: new Border(top: new BorderSide(color: greyTextColor, width: 0.5))))]
|
||||
)));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _buildTiles(context, entry);
|
||||
}
|
||||
}
|
||||
|
||||
class FAQScreen extends StatefulWidget {
|
||||
|
||||
FAQScreen(this.b);
|
||||
final bool b;
|
||||
|
||||
@override State createState() => new FAQScreenState<FAQScreen>(b);
|
||||
}
|
||||
|
||||
class FAQScreenState<T> extends BaseState<FAQScreen> {
|
||||
|
||||
FAQScreenState(this.returnToScanner);
|
||||
final bool returnToScanner;
|
||||
|
||||
@override String getTitle() {
|
||||
return "FAQ";
|
||||
}
|
||||
|
||||
@override getMenuButtons() {
|
||||
return <Widget>[getLogoutButton()];
|
||||
}
|
||||
|
||||
@override String getHint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Метод возвращает ListView с блоками faq.
|
||||
@override Widget getScreenContent() {
|
||||
return new WillPopScope(onWillPop: onWillPop, child: new ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) => new EntryItem(data[index]),
|
||||
itemCount: data.length));
|
||||
}
|
||||
|
||||
onWillPop() {
|
||||
if(returnToScanner) {
|
||||
return startScanner(context, app);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Список с контентом
|
||||
final List<Entry> data = <Entry>[
|
||||
new Entry('РЕГИСТРАЦИЯ', registrationGuide),
|
||||
new Entry('ИСПОЛЬЗОВАНИЕ', usageGuide),
|
||||
new Entry('КОНТАКТЫ ПОДДЕРЖКИ', supportGuide),
|
||||
new Entry('ОБЩАЯ ИНФОРМАЦИЯ', commonGuide)
|
||||
];
|
||||
|
||||
static const String registrationGuide = '''
|
||||
После запуска приложения вы окажетесь на странице регистрации магазина.
|
||||
|
||||
Введите DIN код магазина (выдается партнером/менеджером International Auto Club, дублируется на почту)
|
||||
|
||||
Кликните по кнопке: «Зарегистрировать»
|
||||
Дождитесь подтверждение активации программы, кликом по кнопке «Обновите статус активации» обновите статус.
|
||||
|
||||
После подтверждения запроса на активацию программы Партнером/менеджером кликните по кнопке «Завершить регистрацию», приложение готово к использованию.
|
||||
|
||||
При желании изменить номер кассы, необходимо кликнуть на «значок» верхнем правом углу и вернуться на шаг регистрации.
|
||||
''';
|
||||
|
||||
static const String usageGuide = '''
|
||||
Действие 1:
|
||||
|
||||
При предъявлении покупателем штрих-кода участника системы лояльности, запустите данное приложение.
|
||||
|
||||
На экране появится сканер штрих кодов. Отсканируйте предъявленный штрих-код сканером.
|
||||
|
||||
При успешном сканировании на вашем экране появятся данные партнера.
|
||||
|
||||
Действие 2:
|
||||
|
||||
Необходимо ввести сумму покупки данного покупателя и кликнуть по кнопке «Зафиксировать».
|
||||
|
||||
Всплывет окно подтверждения правильности ввода суммы». В случае правильного ввода суммы, кликните «ДА», сумма будет проведена и вознаграждение будет начислено участнику системы лояльности.
|
||||
|
||||
Если сумма введена с ошибкой, кликните «НЕТ» и Вы вернетесь на шаг ввода суммы и сможете её скорректировать.
|
||||
''';
|
||||
|
||||
static const String supportGuide = '''
|
||||
При некорректной работе приложения AUTO BONUS просьба сразу обратиться по телефону нашей технической поддержки: 8-800-234-6064 (звонок бесплатный) и Вас свяжут с менеджером.
|
||||
|
||||
При звонке приготовьтесь назвать ИНН и наименование вашей организации.
|
||||
|
||||
Рекомендуйте покупателям установить мобильное приложение дисконтной системы International Auto Club AUTO CLUB и получайте новых лояльных покупателей.
|
||||
|
||||
Наш сайт https://www.auto-club.biz
|
||||
''';
|
||||
|
||||
static const String commonGuide = '''
|
||||
Для эффективного считывания штрих-кода участника системы лояльности необходимо камеру сканера поднести так, чтобы в неё не попадали вертикальные полосы рамки.
|
||||
|
||||
Увеличение времени сканирования может произойти из-за черной рамки, в которую помещен штрих-код, так как вертикальные полосы этой рамки расцениваются сканером как часть штрих-кода.
|
||||
''';
|
||||
}
|
||||
@@ -30,6 +30,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"no" : MessageLookupByLibrary.simpleMessage("No"),
|
||||
"purchase_complite" : MessageLookupByLibrary.simpleMessage("A purchase of %s USD was complite"),
|
||||
"registration" : MessageLookupByLibrary.simpleMessage("Registration"),
|
||||
"usage" : MessageLookupByLibrary.simpleMessage("Usage"),
|
||||
"support" : MessageLookupByLibrary.simpleMessage("Support contacts"),
|
||||
"common" : MessageLookupByLibrary.simpleMessage("General information"),
|
||||
"request_sent_wait_activ" : MessageLookupByLibrary.simpleMessage("The activation request for the application has been sent, wait for confirm activation"),
|
||||
"reward" : MessageLookupByLibrary.simpleMessage("Reward"),
|
||||
"scan" : MessageLookupByLibrary.simpleMessage("Scan"),
|
||||
@@ -38,6 +41,66 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sum" : MessageLookupByLibrary.simpleMessage("Sum"),
|
||||
"update_activ_status" : MessageLookupByLibrary.simpleMessage("Update activation status"),
|
||||
"user_name" : MessageLookupByLibrary.simpleMessage("User name"),
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Yes")
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Yes"),
|
||||
"settings" : MessageLookupByLibrary.simpleMessage("Settings"),
|
||||
"help" : MessageLookupByLibrary.simpleMessage("Help"),
|
||||
"logout" : MessageLookupByLibrary.simpleMessage("Exit"),
|
||||
"currency" : MessageLookupByLibrary.simpleMessage("Currency"),
|
||||
"locale" : MessageLookupByLibrary.simpleMessage("Language"),
|
||||
"ruble" : MessageLookupByLibrary.simpleMessage("Ruble"),
|
||||
"dollar" : MessageLookupByLibrary.simpleMessage("Dollar"),
|
||||
"hryvna" : MessageLookupByLibrary.simpleMessage("Hryvna"),
|
||||
"nominative_ruble": MessageLookupByLibrary.simpleMessage("Ruble"),
|
||||
"singular_ruble": MessageLookupByLibrary.simpleMessage("Ruble"),
|
||||
"plural_ruble": MessageLookupByLibrary.simpleMessage("Rubles"),
|
||||
"nominative_dollar": MessageLookupByLibrary.simpleMessage("US Dollar"),
|
||||
"singular_dollar": MessageLookupByLibrary.simpleMessage("US Dollar"),
|
||||
"plural_dollar": MessageLookupByLibrary.simpleMessage("US Dollars"),
|
||||
"nominative_hryvna": MessageLookupByLibrary.simpleMessage("Hryvnia"),
|
||||
"singular_hryvna": MessageLookupByLibrary.simpleMessage("Hryvnia"),
|
||||
"plural_hryvna": MessageLookupByLibrary.simpleMessage("Hryvnia"),
|
||||
"nominative_tenge": MessageLookupByLibrary.simpleMessage("Tenge"),
|
||||
"singular_tenge": MessageLookupByLibrary.simpleMessage("Tenge"),
|
||||
"plural_tenge": MessageLookupByLibrary.simpleMessage("Tenge"),
|
||||
"nominative_euro": MessageLookupByLibrary.simpleMessage("Euro"),
|
||||
"singular_euro": MessageLookupByLibrary.simpleMessage("Euro"),
|
||||
"plural_euro": MessageLookupByLibrary.simpleMessage("Euro"),
|
||||
"registration_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Store log in screen is the first thing you will see after starting the application.
|
||||
|
||||
Enter the store number (DIN). You can look it up in the loyalty program control panel. If you do not have access to the control panel, ask the administrator.
|
||||
|
||||
Click the ""Login"" button.
|
||||
|
||||
Please wait while the administrator activates your request. You can refresh your store activation status by pressing the ""Update activation status"" button.
|
||||
|
||||
After the administrator activates your request, click the ""Complete activation"" button. The application is ready to use.
|
||||
|
||||
If you want to log in as another store, click the Menu button (upper right corner of the screen) and select "Exit".
|
||||
'''),
|
||||
"usage_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Step 1:
|
||||
|
||||
Launch this application and scan your customer's loyalty card using the built in scanner.
|
||||
|
||||
If the scan is successful, the customer's information will appear on the screen.
|
||||
|
||||
Step 2:
|
||||
|
||||
Enter the purchase amount and click the ""Create a purchase"" button.
|
||||
|
||||
In a pop-up window press ""YES"" to confirm the amount and allot the points to a customer.
|
||||
|
||||
If you want to correct the amount, press ""NO"" and you will return back to the purchase screen where you can adjust the amount.
|
||||
'''),
|
||||
"support_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Always recommend your customers to install your loyalty card app, so they can participate in your loyalty program.
|
||||
|
||||
If you have any problems with the application, feel free to contact the support.
|
||||
|
||||
Phone:\n%s\n
|
||||
Our website:\n%s'''),
|
||||
"common_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
To improve barcode scanning quality, adjust the distance between the camera and the barcode so that the border around the barcode (if any) is not visible. Otherwise the vertical lines of the border could be wrongly considered as part of the code.''')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,6 +38,66 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sum" : MessageLookupByLibrary.simpleMessage("Suma"),
|
||||
"update_activ_status" : MessageLookupByLibrary.simpleMessage("Actualizar la condición de activación"),
|
||||
"user_name" : MessageLookupByLibrary.simpleMessage("Un nombre de usario"),
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Si")
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Si"),
|
||||
"settings" : MessageLookupByLibrary.simpleMessage("Las configuraciones"),
|
||||
"help" : MessageLookupByLibrary.simpleMessage("La Ayuda"),
|
||||
"logout" : MessageLookupByLibrary.simpleMessage("Salir"),
|
||||
"currency" : MessageLookupByLibrary.simpleMessage("La Moneda"),
|
||||
"locale" : MessageLookupByLibrary.simpleMessage("La lengua"),
|
||||
"ruble" : MessageLookupByLibrary.simpleMessage("Ruble"),
|
||||
"dollar" : MessageLookupByLibrary.simpleMessage("Dollar"),
|
||||
"hryvna" : MessageLookupByLibrary.simpleMessage("Hryvna"),
|
||||
"nominative_ruble": MessageLookupByLibrary.simpleMessage("Rublo"),
|
||||
"singular_ruble": MessageLookupByLibrary.simpleMessage("Rublo"),
|
||||
"plural_ruble": MessageLookupByLibrary.simpleMessage("Rublos"),
|
||||
"nominative_dollar": MessageLookupByLibrary.simpleMessage("Dólar Dolares"),
|
||||
"singular_dollar": MessageLookupByLibrary.simpleMessage("Dólar Dolares"),
|
||||
"plural_dollar": MessageLookupByLibrary.simpleMessage("Dólar Dolares"),
|
||||
"nominative_hryvna": MessageLookupByLibrary.simpleMessage("Hryvnia"),
|
||||
"singular_hryvna": MessageLookupByLibrary.simpleMessage("Hryvnia"),
|
||||
"plural_hryvna": MessageLookupByLibrary.simpleMessage("Hryvnia"),
|
||||
"nominative_tenge": MessageLookupByLibrary.simpleMessage("Tenge"),
|
||||
"singular_tenge": MessageLookupByLibrary.simpleMessage("Tenge"),
|
||||
"plural_tenge": MessageLookupByLibrary.simpleMessage("Tenge"),
|
||||
"nominative_euro": MessageLookupByLibrary.simpleMessage("Euro"),
|
||||
"singular_euro": MessageLookupByLibrary.simpleMessage("Euro"),
|
||||
"plural_euro": MessageLookupByLibrary.simpleMessage("Euro"),
|
||||
"registration_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Store log in screen is the first thing you will see after starting the application.
|
||||
|
||||
Enter the store number (DIN). You can look it up in the loyalty program control panel. If you do not have access to the control panel, ask the administrator.
|
||||
|
||||
Click the ""Login"" button.
|
||||
|
||||
Please wait while the administrator activates your request. You can refresh your store activation status by pressing the ""Update activation status"" button.
|
||||
|
||||
After the administrator activates your request, click the ""Complete activation"" button. The application is ready to use.
|
||||
|
||||
If you want to log in as another store, click the Menu button (upper right corner of the screen) and select "Exit".
|
||||
'''),
|
||||
"usage_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Step 1:
|
||||
|
||||
Launch this application and scan your customer's loyalty card using the built in scanner.
|
||||
|
||||
If the scan is successful, the customer's information will appear on the screen.
|
||||
|
||||
Step 2:
|
||||
|
||||
Enter the purchase amount and click the ""Create a purchase"" button.
|
||||
|
||||
In a pop-up window press ""YES"" to confirm the amount and allot the points to a customer.
|
||||
|
||||
If you want to correct the amount, press ""NO"" and you will return back to the purchase screen where you can adjust the amount.
|
||||
'''),
|
||||
"support_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Always recommend your customers to install your loyalty card app, so they can participate in your loyalty program.
|
||||
|
||||
If you have any problems with the application, feel free to contact the support.
|
||||
|
||||
Phone:\n%s\n
|
||||
Our website:\n%s'''),
|
||||
"common_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
To improve barcode scanning quality, adjust the distance between the camera and the barcode so that the border around the barcode (if any) is not visible. Otherwise the vertical lines of the border could be wrongly considered as part of the code.''')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,11 +25,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"carry_purchase" : MessageLookupByLibrary.simpleMessage("Проведение покупки"),
|
||||
"complite_activ" : MessageLookupByLibrary.simpleMessage("Завершить регистрацию"),
|
||||
"complite_purchase" : MessageLookupByLibrary.simpleMessage("Завершить покупку"),
|
||||
"confirm_purchase" : MessageLookupByLibrary.simpleMessage("Вы подтверждаете покупку на %s рублей"),
|
||||
"confirm_purchase" : MessageLookupByLibrary.simpleMessage("Вы подтверждаете покупку на %s %s"),
|
||||
"confirmation" : MessageLookupByLibrary.simpleMessage("Подтверждение"),
|
||||
"no" : MessageLookupByLibrary.simpleMessage("Нет"),
|
||||
"purchase_complite" : MessageLookupByLibrary.simpleMessage("Покупка на сумму %s рублей проведена"),
|
||||
"purchase_complite" : MessageLookupByLibrary.simpleMessage("Покупка на сумму %s %s проведена"),
|
||||
"registration" : MessageLookupByLibrary.simpleMessage("Регистрация"),
|
||||
"usage" : MessageLookupByLibrary.simpleMessage("Использование"),
|
||||
"support" : MessageLookupByLibrary.simpleMessage("Контакты поддержки"),
|
||||
"common" : MessageLookupByLibrary.simpleMessage("Общая информация"),
|
||||
"request_sent_wait_activ" : MessageLookupByLibrary.simpleMessage("Запрос на активацию приложения отправлен, дождитесь подтверждения активации администратором"),
|
||||
"reward" : MessageLookupByLibrary.simpleMessage("Вознаграждение"),
|
||||
"scan" : MessageLookupByLibrary.simpleMessage("Сканировать"),
|
||||
@@ -38,6 +41,64 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sum" : MessageLookupByLibrary.simpleMessage("Сумма"),
|
||||
"update_activ_status" : MessageLookupByLibrary.simpleMessage("Обновить статус активации"),
|
||||
"user_name" : MessageLookupByLibrary.simpleMessage("ФИО"),
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Да")
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Да"),
|
||||
"settings" : MessageLookupByLibrary.simpleMessage("Настройки"),
|
||||
"help" : MessageLookupByLibrary.simpleMessage("Справка"),
|
||||
"logout" : MessageLookupByLibrary.simpleMessage("Выход"),
|
||||
"currency" : MessageLookupByLibrary.simpleMessage("Валюта"),
|
||||
"locale" : MessageLookupByLibrary.simpleMessage("Язык"),
|
||||
"nominative_ruble": MessageLookupByLibrary.simpleMessage("Рубль"),
|
||||
"singular_ruble": MessageLookupByLibrary.simpleMessage("Рубля"),
|
||||
"plural_ruble": MessageLookupByLibrary.simpleMessage("Рублей"),
|
||||
"nominative_dollar": MessageLookupByLibrary.simpleMessage("Доллар США"),
|
||||
"singular_dollar": MessageLookupByLibrary.simpleMessage("Доллара США"),
|
||||
"plural_dollar": MessageLookupByLibrary.simpleMessage("Долларов США"),
|
||||
"nominative_hryvna": MessageLookupByLibrary.simpleMessage("Гривна"),
|
||||
"singular_hryvna": MessageLookupByLibrary.simpleMessage("Гривны"),
|
||||
"plural_hryvna": MessageLookupByLibrary.simpleMessage("Гривен"),
|
||||
"nominative_tenge": MessageLookupByLibrary.simpleMessage("Тенге"),
|
||||
"singular_tenge": MessageLookupByLibrary.simpleMessage("Тенге"),
|
||||
"plural_tenge": MessageLookupByLibrary.simpleMessage("Тенге"),
|
||||
"nominative_euro": MessageLookupByLibrary.simpleMessage("Евро"),
|
||||
"singular_euro": MessageLookupByLibrary.simpleMessage("Евро"),
|
||||
"plural_euro": MessageLookupByLibrary.simpleMessage("Евро"),
|
||||
"registration_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
После запуска приложения вы окажетесь на странице регистрации магазина.
|
||||
|
||||
Введите DIN код магазина (выдается партнером/менеджером International Auto Club, дублируется на почту)
|
||||
|
||||
Кликните по кнопке: «Зарегистрировать»
|
||||
Дождитесь подтверждение активации программы, кликом по кнопке «Обновите статус активации» обновите статус.
|
||||
|
||||
После подтверждения запроса на активацию программы Партнером/менеджером кликните по кнопке «Завершить регистрацию», приложение готово к использованию.
|
||||
|
||||
При желании изменить номер кассы, необходимо кликнуть на «значок» верхнем правом углу и вернуться на шаг регистрации.
|
||||
'''),
|
||||
"usage_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Шаг 1:
|
||||
|
||||
Запустите приложение для сканирования карты участника системы лояльности.
|
||||
|
||||
При успешном сканировании на вашем экране появятся данные покупателя.
|
||||
|
||||
Шаг 2:
|
||||
|
||||
Введите сумму покупки данного покупателя и нажмите на кнопку «Проведение покупки».
|
||||
|
||||
Во всплывающем окне нажмите ""ДА"", для подтверждения суммы покупки
|
||||
|
||||
Если вы хотите поправить сумму, нажмите «НЕТ» и Вы вернетесь на экран покупки и сможете её скорректировать.
|
||||
'''),
|
||||
"support_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Рекомендуйте покупателям установить мобильное приложение дисконтной системы и получайте новых лояльных покупателей.
|
||||
|
||||
При некорректной работе приложения просьба сразу обратиться по телефону нашей технической поддержки.
|
||||
|
||||
Телефон:\n%s\n
|
||||
Наш сайт:\n%s
|
||||
'''),
|
||||
"common_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Для эффективного считывания штрих-кода карты участника системы лояльности необходимо камеру сканера поднести так, чтобы в неё не попадали вертикальные полосы рамки (если она есть). Они расцениваются сканером как часть штрих-кода.
|
||||
''')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,11 +25,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"carry_purchase" : MessageLookupByLibrary.simpleMessage("Проведення покупки"),
|
||||
"complite_activ" : MessageLookupByLibrary.simpleMessage("Завершити реєстрацію"),
|
||||
"complite_purchase" : MessageLookupByLibrary.simpleMessage("Завершити купівлю"),
|
||||
"confirm_purchase" : MessageLookupByLibrary.simpleMessage("Ви підтверджуєте покупку на %s гривень"),
|
||||
"confirm_purchase" : MessageLookupByLibrary.simpleMessage("Ви підтверджуєте покупку на %s %s"),
|
||||
"confirmation" : MessageLookupByLibrary.simpleMessage("Підтвердження"),
|
||||
"no" : MessageLookupByLibrary.simpleMessage("Ні"),
|
||||
"purchase_complite" : MessageLookupByLibrary.simpleMessage("Купівля на суму %s гривень проведена"),
|
||||
"purchase_complite" : MessageLookupByLibrary.simpleMessage("Купівля на суму %s %s проведена"),
|
||||
"registration" : MessageLookupByLibrary.simpleMessage("Реєстрація"),
|
||||
"usage" : MessageLookupByLibrary.simpleMessage("Використання"),
|
||||
"support" : MessageLookupByLibrary.simpleMessage("Контакти підтримки"),
|
||||
"common" : MessageLookupByLibrary.simpleMessage("Загальна інформація"),
|
||||
"request_sent_wait_activ" : MessageLookupByLibrary.simpleMessage("Запит на активацію додатку відправлений, дочекайтеся підтвердження активації адміністратором"),
|
||||
"reward" : MessageLookupByLibrary.simpleMessage("Винагорода"),
|
||||
"scan" : MessageLookupByLibrary.simpleMessage("Сканувати"),
|
||||
@@ -38,6 +41,66 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sum" : MessageLookupByLibrary.simpleMessage("Сума"),
|
||||
"update_activ_status" : MessageLookupByLibrary.simpleMessage("Оновити статус активації"),
|
||||
"user_name" : MessageLookupByLibrary.simpleMessage("ПІБ"),
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Так")
|
||||
"yes" : MessageLookupByLibrary.simpleMessage("Так"),
|
||||
"help" : MessageLookupByLibrary.simpleMessage("Допомога"),
|
||||
"settings" : MessageLookupByLibrary.simpleMessage("Налаштування"),
|
||||
"logout" : MessageLookupByLibrary.simpleMessage("Вихід"),
|
||||
"currency" : MessageLookupByLibrary.simpleMessage("Валюта"),
|
||||
"locale" : MessageLookupByLibrary.simpleMessage("Мова"),
|
||||
"nominative_ruble": MessageLookupByLibrary.simpleMessage("Рубль"),
|
||||
"singular_ruble": MessageLookupByLibrary.simpleMessage("Рубль"),
|
||||
"plural_ruble": MessageLookupByLibrary.simpleMessage("Рубль"),
|
||||
"nominative_dollar": MessageLookupByLibrary.simpleMessage("Доллар США"),
|
||||
"singular_dollar": MessageLookupByLibrary.simpleMessage("Доллар США"),
|
||||
"plural_dollar": MessageLookupByLibrary.simpleMessage("Доллар США"),
|
||||
"nominative_hryvna": MessageLookupByLibrary.simpleMessage("Гривня"),
|
||||
"singular_hryvna": MessageLookupByLibrary.simpleMessage("Гривня"),
|
||||
"plural_hryvna": MessageLookupByLibrary.simpleMessage("Гривня"),
|
||||
"nominative_tenge": MessageLookupByLibrary.simpleMessage("Тенге"),
|
||||
"singular_tenge": MessageLookupByLibrary.simpleMessage("Тенге"),
|
||||
"plural_tenge": MessageLookupByLibrary.simpleMessage("Тенге"),
|
||||
"nominative_euro": MessageLookupByLibrary.simpleMessage("Євро"),
|
||||
"singular_euro": MessageLookupByLibrary.simpleMessage("Євро"),
|
||||
"plural_euro": MessageLookupByLibrary.simpleMessage("Євро"),
|
||||
"registration_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Після запуску програми ви опинитеся на сторінці реєстрації магазина.
|
||||
|
||||
Введіть DIN код магазину (видається при підключенні до системи лояльності)
|
||||
|
||||
Натисніть на кнопку «Зареєструвати»
|
||||
Дочекайтеся підтвердження активації програми, натисканням на кнопку «Оновлення статус активації» поновіть статус.
|
||||
|
||||
Після підтвердження запиту на активацію програми Партнером / менеджером клікніть по кнопці «Завершити реєстрацію», додаток готове до використання.
|
||||
|
||||
При бажанні змінити номер каси, необхідно натиснути на кнопку Меню (верхній правий кут екрану) і вибрати "Вихід".
|
||||
'''),
|
||||
"usage_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Крок 1:
|
||||
|
||||
При пред'явленні покупцем картки учасника системи лояльності, запустіть цю програму.
|
||||
|
||||
На екрані з'явиться сканер штрих кодів. Відскануте штрих-код карти сканером.
|
||||
|
||||
При успішному скануванні на вашому екрані з'являться дані покупця.
|
||||
|
||||
Крок 2:
|
||||
|
||||
Необхідно ввести суму покупки даного покупця і клікнути на кнопку «Проведення покупки».
|
||||
|
||||
Спливе вікно підтвердження правильності введення суми. У разі правильного введення суми, натисніть «ТАК», сума буде проведена і винагороду буде нараховано учаснику системи лояльності.
|
||||
|
||||
Якщо сума введена з помилкою, натисніть «НІ» і Ви повернетеся на крок введення суми і зможете її скорегувати.
|
||||
'''),
|
||||
"support_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Рекомендуйте покупцям встановити мобільний додаток дисконтної системи і отримуйте нових лояльних покупців.
|
||||
|
||||
При некоректній роботі програми прохання відразу звернутися за телефоном нашої технічної підтримки.
|
||||
|
||||
Телефон:\n%s\n
|
||||
Наш сайт:\n%s
|
||||
'''),
|
||||
"common_guide": MessageLookupByLibrary.simpleMessage('''
|
||||
Для ефективного зчитування штрих-коду карти учасника системи лояльності необхідно камеру сканера піднести так, щоб в неї не потрапляли вертикальні смуги рамки. Вони розцінюються сканером як частина штрих-коду.
|
||||
''')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,44 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'splash.dart';
|
||||
import 'package:checker/screens/splash.dart';
|
||||
import 'consts.dart';
|
||||
import 'strings.dart';
|
||||
import 'common.dart';
|
||||
import 'dart:async';
|
||||
|
||||
class StringsLocalizationDelegate extends LocalizationsDelegate<StringsLocalization> {
|
||||
|
||||
@override
|
||||
Future<StringsLocalization> load(Locale locale) async {
|
||||
return StringsLocalization.load(await platform.invokeMethod("getLocale"));
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReload(LocalizationsDelegate<StringsLocalization> old) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Точка входа в приложение.
|
||||
void main() {
|
||||
runApp(new Checker());
|
||||
}
|
||||
|
||||
class Checker extends StatefulWidget {
|
||||
@override CheckerState createState() => new CheckerState();
|
||||
}
|
||||
|
||||
class CheckerState extends State<Checker> {
|
||||
|
||||
// TODO: Запрашивать appName у платформы
|
||||
class Checker extends StatelessWidget {
|
||||
@override Widget build(BuildContext context) {
|
||||
return new MaterialApp(
|
||||
title: appName,
|
||||
home: new SplashScreen(),
|
||||
localizationsDelegates: getLocalizationsDelegate()
|
||||
);
|
||||
}
|
||||
|
||||
getLocalizationsDelegate() {
|
||||
return <StringsLocalizationDelegate>[new StringsLocalizationDelegate()];
|
||||
home: new SplashScreen());
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ final httpClient = createHttpClient();
|
||||
// Попытка создать токен для кассы.
|
||||
// В случае если токен для кассы уже существует, вернется ошибка 409.
|
||||
// На сервере есть ограничение в 40 токенов.
|
||||
createToken(String merchantId, String posID) async {
|
||||
createToken(String merchantId, String posID, String locale) async {
|
||||
|
||||
// Поле description - необязательное.
|
||||
var body = {
|
||||
@@ -16,15 +16,15 @@ createToken(String merchantId, String posID) async {
|
||||
'pos': posID,
|
||||
};
|
||||
|
||||
return httpClient.post(url + 'tokens/?_dmapptoken=' + appToken, body: body);
|
||||
return httpClient.post(url + 'tokens/?_dmapptoken=' + appToken, body: body, headers: {'Accept-Language': locale});
|
||||
}
|
||||
|
||||
// Проверка статуса токена. В ответе приходит параметр active, который может быть либо true, либо false,.
|
||||
checkTokenStatus(String token) async {
|
||||
return httpClient.get(url + 'tokens/' + token + '?_dmapptoken=' + appToken);
|
||||
checkTokenStatus(String token, String locale) async {
|
||||
return httpClient.get(url + 'tokens/' + token + '?_dmapptoken=' + appToken, headers: {'Accept-Language': locale});
|
||||
}
|
||||
|
||||
// Удаление токена на сервере.
|
||||
deleteToken(String token) async {
|
||||
return httpClient.delete(url + 'tokens/' + token + '?_dmapptoken=' + appToken);
|
||||
deleteToken(String token, String locale) async {
|
||||
return httpClient.delete(url + 'tokens/' + token + '?_dmapptoken=' + appToken, headers: {'Accept-Language': locale});
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
|
||||
/// Экран проведения покупки.
|
||||
class PurchaseSuccessScreen extends StatefulWidget {
|
||||
|
||||
PurchaseSuccessScreen(this.val, this.name);
|
||||
final String val;
|
||||
final String name;
|
||||
|
||||
@override State createState() => new PurchaseSuccessScreenState(val, name);
|
||||
}
|
||||
|
||||
class PurchaseSuccessScreenState<T> extends BaseState<PurchaseSuccessScreen> {
|
||||
|
||||
PurchaseSuccessScreenState(this.sum, this.username);
|
||||
|
||||
String sum;
|
||||
String username;
|
||||
|
||||
@override getMenuButtons() {
|
||||
return <Widget>[getFaqButton(), getLogoutButton()];
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.carryingPurchase();
|
||||
}
|
||||
|
||||
@override String getHint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(children: <Widget>[
|
||||
getValueWithDescription(StringsLocalization.buyer(), username),
|
||||
getSuccessMessage(),
|
||||
new Expanded(child: new Center()),
|
||||
wrapButton(getScreenMargins(74.0), getScanButton())
|
||||
]);
|
||||
}
|
||||
|
||||
getScreenMargins(double bottom) {
|
||||
double side = 42.0;
|
||||
return new EdgeInsets.only(bottom: bottom, left: side, right: side);
|
||||
}
|
||||
|
||||
getScanButton() {
|
||||
String title = StringsLocalization.scan();
|
||||
return buildRaisedButton(title, () => startScanner(context, app));
|
||||
}
|
||||
|
||||
getSuccessMessage() {
|
||||
return new Row(children: <Widget>[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(getMessageTitle(), textAlign: TextAlign.center,
|
||||
style: new TextStyle(fontWeight: FontWeight.bold, color: tokenActiveTextColor)))))]);
|
||||
}
|
||||
|
||||
getMessageTitle() {
|
||||
return StringsLocalization.purchaseCompleted(sum);
|
||||
}
|
||||
|
||||
}
|
||||
51
lib/screens/currencies.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:checker/base/base_screen.dart';
|
||||
import 'package:checker/base/settings_base_state.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
|
||||
class CurrenciesScreen extends BaseScreen {
|
||||
|
||||
CurrenciesScreen(helper, app) : super(helper, app);
|
||||
|
||||
@override State createState() => new _CurrenciesState(helper, app);
|
||||
}
|
||||
|
||||
class _CurrenciesState extends SettingsBaseState<CurrenciesScreen> {
|
||||
|
||||
List<int> currencies = const [643, 840, 980, 978, 398];
|
||||
|
||||
_CurrenciesState(SqliteHelper helper, String app) : super(helper, app);
|
||||
|
||||
|
||||
@override
|
||||
List<String> getOptions() {
|
||||
|
||||
String ruble = StringsLocalization.nominativeRuble();
|
||||
String dollar = StringsLocalization.nominativeDollar();
|
||||
String hryvna = StringsLocalization.nominativeHryvna();
|
||||
String euro = StringsLocalization.nominativeEuro();
|
||||
String tenge = StringsLocalization.nominativeTenge();
|
||||
|
||||
return [ruble, dollar, hryvna, euro, tenge];
|
||||
}
|
||||
|
||||
@override
|
||||
void getSelectedValue() {
|
||||
helper.getCurrency().then((currency) {
|
||||
setState(() {
|
||||
selectedItem = currencies.indexOf(currency);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
String getTitle() {
|
||||
return StringsLocalization.settings();
|
||||
}
|
||||
|
||||
@override
|
||||
saveOption() async {
|
||||
await helper.saveCurrency(currencies[selectedItem]);
|
||||
}
|
||||
}
|
||||
143
lib/screens/faq.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:checker/resources.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/common.dart';
|
||||
|
||||
/// Класс содержит заголовки и текст блоков FAQ.
|
||||
class Entry {
|
||||
Entry(this.title, this.text);
|
||||
final String title;
|
||||
final String text;
|
||||
}
|
||||
|
||||
class EntryItem extends StatelessWidget {
|
||||
|
||||
const EntryItem(this.entry);
|
||||
final Entry entry;
|
||||
|
||||
Widget _buildTiles(BuildContext context, Entry root) {
|
||||
EdgeInsets margin = new EdgeInsets.only(left: 20.0, right: 20.0);
|
||||
TextStyle titleStyle = Theme.of(context).textTheme.button.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: faqTitlesColor);
|
||||
return new Container(margin: margin, child: new Card(
|
||||
child: new ExpansionTile(
|
||||
key: new PageStorageKey<Entry>(root),
|
||||
title:new Text(
|
||||
root.title,
|
||||
style: titleStyle),
|
||||
children: [
|
||||
new Container(
|
||||
margin: margin,
|
||||
padding: new EdgeInsets.only(top: 12.0, bottom: 20.0),
|
||||
child: new Text(
|
||||
root.text,
|
||||
style: new TextStyle(
|
||||
fontWeight: FontWeight.w300,
|
||||
color: faqGrey,
|
||||
fontSize: 14.0)
|
||||
),
|
||||
decoration: new BoxDecoration(
|
||||
border: new Border(
|
||||
top: new BorderSide(
|
||||
color: greyTextColor,
|
||||
width: 0.5)
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _buildTiles(context, entry);
|
||||
}
|
||||
}
|
||||
|
||||
class FAQScreen extends StatefulWidget {
|
||||
|
||||
FAQScreen(this.b);
|
||||
final bool b;
|
||||
|
||||
@override State createState() => new FAQScreenState<FAQScreen>(b);
|
||||
}
|
||||
|
||||
class FAQScreenState<T> extends BaseState<FAQScreen> {
|
||||
|
||||
FAQScreenState(this.returnToScanner);
|
||||
|
||||
bool returnToScanner;
|
||||
|
||||
List<Entry> data;
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.help();
|
||||
}
|
||||
|
||||
@override String getHintString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override Widget build(BuildContext context) {
|
||||
if (app == null) {
|
||||
platform.invokeMethod('getFlavor').then((flavor) {
|
||||
initPhoneAndUrl().then((_) {
|
||||
setState(() {
|
||||
app = flavor;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return new Scaffold(appBar: getAppBar(), body: getScreenContent());
|
||||
}
|
||||
|
||||
Future initPhoneAndUrl() async {
|
||||
initHelp(await platform.invokeMethod('getSupportPhone'),
|
||||
await platform.invokeMethod('getSupportUrl'));
|
||||
}
|
||||
|
||||
void initHelp(String phone, String url) {
|
||||
data = <Entry>[
|
||||
new Entry(StringsLocalization.registration(), StringsLocalization.registrationGuide()),
|
||||
new Entry(StringsLocalization.usage(), StringsLocalization.usageGuide()),
|
||||
new Entry(StringsLocalization.support(), StringsLocalization.supportGuide(phone, url)),
|
||||
new Entry(StringsLocalization.common(), StringsLocalization.commonGuide())
|
||||
];
|
||||
}
|
||||
|
||||
@override List<Widget> getMenuButtons() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Метод возвращает ListView с блоками faq.
|
||||
@override Widget getScreenContent() {
|
||||
if (data == null) {
|
||||
return new Container(
|
||||
decoration: new BoxDecoration(
|
||||
image: new DecorationImage(
|
||||
image: new ExactAssetImage(Resources.getSplash(app)),
|
||||
fit: BoxFit.cover)));
|
||||
} else {
|
||||
return new WillPopScope(onWillPop: onWillPop, child: new ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) =>
|
||||
new EntryItem(data[index]),
|
||||
itemCount: data.length));
|
||||
}
|
||||
}
|
||||
|
||||
onWillPop() {
|
||||
if(returnToScanner) {
|
||||
return startScanner(context, app, helper);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:convert'; // Пакет для обработки json с ответом от сервера.
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:checker/base/base_screen.dart';
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class FinishRegistrationScreen extends StatefulWidget {
|
||||
@override State createState() => new _RegistrationScreenState();
|
||||
class FinishRegistrationScreen extends BaseScreen {
|
||||
|
||||
FinishRegistrationScreen(helper, app) : super(helper, app);
|
||||
|
||||
@override State createState() => new RegistrationScreenState(helper, app);
|
||||
}
|
||||
|
||||
class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
class RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
|
||||
RegistrationScreenState(SqliteHelper helper, String app) {
|
||||
this.helper = helper;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
bool _tokenActive = false;
|
||||
String _merchantID = '';
|
||||
|
||||
_RegistrationScreenState() {
|
||||
if (textFieldValue == "") {
|
||||
getSavedMerchantID();
|
||||
@override Widget build(BuildContext context) {
|
||||
if (_merchantID == '') {
|
||||
helper.getMerchantID().then((result) {
|
||||
setState(() {
|
||||
_merchantID = result;
|
||||
});
|
||||
});
|
||||
}
|
||||
return getMainWidget();
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.registration();
|
||||
}
|
||||
|
||||
@override getHint() {
|
||||
return StringsLocalization.idStore();
|
||||
@override getHintString() {
|
||||
return _merchantID;
|
||||
}
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
@@ -47,13 +62,12 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
// Если нет, то отправляется запрос на проверку статуса токена.
|
||||
handleTap() async {
|
||||
if (_tokenActive) {
|
||||
startScanner(context, app);
|
||||
startScanner(context, app, helper);
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
checkTokenStatus(token).then((response) {
|
||||
|
||||
print(response.body);
|
||||
String token = await helper.getToken();
|
||||
String locale = await helper.getLocale();
|
||||
checkTokenStatus(token, locale).then((response) {
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
|
||||
// Обновить экран, заменить сообщение о необходимости активации токена, на сообщние о том, что токен активен.
|
||||
@@ -70,23 +84,16 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
}
|
||||
|
||||
@override getTextWidget() {
|
||||
return new Row(children: <Widget>[new Text(_merchantID != null ? _merchantID : '',
|
||||
style: new TextStyle(color: Colors.black, fontSize: 16.0))]);
|
||||
}
|
||||
|
||||
/// Достаем сохраненный в SharedPreferences merchantID.
|
||||
getSavedMerchantID() {
|
||||
platform.invokeMethod('getMerchantID').then((result) {
|
||||
setState(() {
|
||||
_merchantID = result;
|
||||
print('merchanID: ${_merchantID}');
|
||||
});
|
||||
});
|
||||
return new Row(
|
||||
children: <Widget>[new Text(_merchantID != null ? _merchantID : '',
|
||||
style: new TextStyle(color: Colors.black, fontSize: 16.0))
|
||||
]);
|
||||
}
|
||||
|
||||
/// Метод возвращает контейнер с текстом сообщения и бэкграундом.
|
||||
getMessage() {
|
||||
return new Container(height: _tokenActive ? 72.0 : 108.0, decoration: _getDecorationForMessageField(),
|
||||
return new Container(height: _tokenActive ? 72.0 : 108.0,
|
||||
decoration: _getDecorationForMessageField(),
|
||||
margin: new EdgeInsets.only(top: 20.0, left: 12.0, right: 12.0),
|
||||
padding: new EdgeInsets.only(bottom: 22.0, left: 14.0, right: 14.0),
|
||||
child: new Center(child: getMessageTextWidget()));
|
||||
@@ -95,8 +102,11 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
/// Метод возвращает виджет с текстом сообщения, всеми его привязками и стилями.
|
||||
getMessageTextWidget() {
|
||||
return new Text(getMessageString(), textAlign: TextAlign.center,
|
||||
style: new TextStyle(height: 1.5, fontWeight: FontWeight.bold, fontSize: 14.0,
|
||||
color: _tokenActive ? tokenActiveTextColor : tokenActivateTextColor));
|
||||
style: new TextStyle(
|
||||
height: 1.5, fontWeight: FontWeight.bold, fontSize: 14.0,
|
||||
color: _tokenActive
|
||||
? tokenActiveTextColor
|
||||
: tokenActivateTextColor));
|
||||
}
|
||||
|
||||
/// Получаем текст сообщения, в зависимости от статуса активации.
|
||||
@@ -109,7 +119,8 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
/// Фоновое изображение для сообщения.
|
||||
Decoration _getDecorationForMessageField() {
|
||||
return new BoxDecoration(image: new DecorationImage(
|
||||
image: new ExactAssetImage(_tokenActive ? active_token_bg_png : activate_token_bg_png),
|
||||
image: new ExactAssetImage(
|
||||
_tokenActive ? active_token_bg_png : activate_token_bg_png),
|
||||
fit: _tokenActive ? BoxFit.fitWidth : BoxFit.fill));
|
||||
}
|
||||
}
|
||||
62
lib/screens/languages.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
import 'package:checker/base/base_screen.dart';
|
||||
import 'package:checker/base/settings_base_state.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class LanguagesScreen extends BaseScreen {
|
||||
|
||||
LanguagesScreen(helper, app) : super(helper, app);
|
||||
|
||||
@override State createState() => new LanguagesState(helper, app);
|
||||
}
|
||||
|
||||
class LanguagesState extends SettingsBaseState<LanguagesScreen> {
|
||||
|
||||
LanguagesState(SqliteHelper helper, String app) : super(helper, app);
|
||||
|
||||
List<String> languages = const ['ru', 'en', 'ua', 'es'];
|
||||
|
||||
@override
|
||||
List<String> getOptions() {
|
||||
|
||||
List<String> list = new List();
|
||||
|
||||
for (String code in languages) {
|
||||
list.add(getLocaleTitle(code));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
String getTitle() {
|
||||
return StringsLocalization.locale();
|
||||
}
|
||||
|
||||
@override
|
||||
saveOption() async {
|
||||
await helper.saveLocale(languages[selectedItem]);
|
||||
Intl.defaultLocale = languages[selectedItem];
|
||||
await StringsLocalization.load(languages[selectedItem]);
|
||||
}
|
||||
|
||||
@override
|
||||
void getSelectedValue() {
|
||||
helper.getLocale().then((locale) {
|
||||
if (locale == null) {
|
||||
platform.invokeMethod('getLocale').then((locale) {
|
||||
setState(() {
|
||||
selectedItem == locale;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
selectedItem = getOptions().indexOf(getLocaleTitle(locale));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
|
||||
import 'resources.dart';
|
||||
import 'strings.dart';
|
||||
import 'common.dart';
|
||||
import 'consts.dart';
|
||||
import 'network.dart';
|
||||
import 'base_state.dart';
|
||||
import 'purchase_success.dart';
|
||||
import 'package:checker/resources.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/screens/purchase_success.dart';
|
||||
|
||||
/// Экран проведения покупки.
|
||||
class PurchaseScreen extends StatefulWidget {
|
||||
@@ -32,7 +33,23 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
PurchaseScreenState(String userString, String card) {
|
||||
this.user = JSON.decode(userString);
|
||||
this.card = card;
|
||||
}
|
||||
|
||||
@override Widget build(BuildContext ctx) {
|
||||
if (helper == null) {
|
||||
helper = new SqliteHelper();
|
||||
helper.open().then((_) {
|
||||
if (app == null) {
|
||||
platform.invokeMethod('getFlavor').then((flavor) {
|
||||
app = flavor;
|
||||
setState(() {
|
||||
getLoyalty(user['loyalty_url']);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return getMainWidget();
|
||||
}
|
||||
|
||||
bool purchaseInProgress = false;
|
||||
@@ -43,7 +60,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(
|
||||
children: <Widget>[new Expanded(child: new ListView(children: <Widget>[
|
||||
getValueWithDescription(StringsLocalization.userName(), user['first_name'] == null ? '' : user['first_name']),
|
||||
getValueWithDescription(StringsLocalization.buyer(), user['first_name'] == null ? '' : user['first_name']),
|
||||
getValueWithDescription(StringsLocalization.card(), card),
|
||||
getValueWithDescription(StringsLocalization.reward(), loyalty),
|
||||
getHintLabel(),
|
||||
@@ -69,7 +86,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
child: new Text(
|
||||
title,
|
||||
style: new TextStyle(color: textColor)),
|
||||
onPressed: () => startScanner(context, app)),
|
||||
onPressed: () => startScanner(context, app, helper)),
|
||||
decoration: new BoxDecoration(
|
||||
border: new Border.all(color: Resources.getButtonColor(app), width: 1.0),
|
||||
borderRadius: new BorderRadius.all(new Radius.circular(4.0))));
|
||||
@@ -79,19 +96,15 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
return StringsLocalization.carryingPurchase();
|
||||
}
|
||||
|
||||
@override getHint() {
|
||||
@override getHintString() {
|
||||
return StringsLocalization.sum();
|
||||
}
|
||||
|
||||
@override getMenuButtons() {
|
||||
return <Widget>[getFaqButton(), getLogoutButton()];
|
||||
}
|
||||
|
||||
@override getTextWidget() {
|
||||
return new TextField(
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration.collapsed(
|
||||
hintText: getHint(),
|
||||
hintText: getHintString(),
|
||||
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)
|
||||
),
|
||||
controller: controller,
|
||||
@@ -109,11 +122,13 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
String token = await helper.getToken();
|
||||
String locale = await helper.getLocale();
|
||||
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken $appToken',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
'Authorization': 'dmtoken ${token}',
|
||||
'Accept-Language': locale
|
||||
};
|
||||
|
||||
httpClient.get(url, headers: headers).then((response) {
|
||||
@@ -127,8 +142,8 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
this.loyalty = '${user['discount']}%';
|
||||
} else {
|
||||
List amountToBonus = bonuses['amount_to_bonus'];
|
||||
double loyalityVal = (double.parse(amountToBonus[1]) / amountToBonus[0]) * 100;
|
||||
this.loyalty = '${loyalityVal.toStringAsFixed(0)}%';
|
||||
double loyaltyVal = (double.parse(amountToBonus[1]) / amountToBonus[0]) * 100;
|
||||
this.loyalty = '${loyaltyVal.toStringAsFixed(0)}%';
|
||||
}
|
||||
});
|
||||
}).catchError((error) {
|
||||
@@ -137,6 +152,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Переделать? чтобы работало хорошо
|
||||
String _cleanupNumber(String text){
|
||||
String tmp = text
|
||||
.replaceAll(' ', '')
|
||||
@@ -175,9 +191,11 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
onPurchaseClick() {
|
||||
String val = _parseSum(controller.text);
|
||||
helper.getCurrency().then((currency) {
|
||||
print(currency.toString());
|
||||
showDialog(context: context, child: new AlertDialog(
|
||||
title: new Text(StringsLocalization.confirmation()),
|
||||
content: new Text(StringsLocalization.confirmPurchase(val)),
|
||||
content: new Text(StringsLocalization.confirmPurchase(val, currency)),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: new Text(StringsLocalization.no()),
|
||||
@@ -192,6 +210,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
},
|
||||
)
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
purchase(String sumTotal) async {
|
||||
@@ -199,34 +218,46 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
if (!purchaseInProgress) {
|
||||
purchaseInProgress = true;
|
||||
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
platform.invokeMethod('getDocID').then((result) {
|
||||
String token = await helper.getToken();
|
||||
String locale = await helper.getLocale();
|
||||
helper.getMerchantID().then((result) {
|
||||
|
||||
String url = user['purchases_url'];
|
||||
|
||||
helper.getCurrency().then((currency) {
|
||||
var body = {
|
||||
'doc_id': result,
|
||||
'curr_iso_code': '643',
|
||||
'curr_iso_code': currency.toString(),
|
||||
'commit': 'true',
|
||||
'sum_total': sumTotal
|
||||
};
|
||||
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken $appToken',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
'Authorization': 'dmtoken ${token}',
|
||||
'Accept-Language': locale
|
||||
};
|
||||
|
||||
httpClient.post(url, body: body, headers: headers).then((response) {
|
||||
|
||||
print(response.body);
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
pushRoute(context, new PurchaseSuccessScreen(sumTotal, user['first_name'] == null ? '' : user['first_name']));
|
||||
|
||||
if (parsedMap.containsKey('errors')) {
|
||||
List<String> errors = parsedMap['errors'];
|
||||
Scaffold.of(context).showSnackBar(new SnackBar(content: new Text(errors[0])));
|
||||
} else {
|
||||
pushRouteReplacement(context, new PurchaseSuccessScreen(sumTotal, user['first_name'] == null ? '' : user['first_name'], helper, app));
|
||||
}
|
||||
|
||||
}).catchError((error) {
|
||||
purchaseInProgress = false;
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
92
lib/screens/purchase_success.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Экран проведения покупки.
|
||||
class PurchaseSuccessScreen extends StatefulWidget {
|
||||
|
||||
PurchaseSuccessScreen(this.val, this.name, this.helper, this.app);
|
||||
|
||||
final String val;
|
||||
final String name;
|
||||
final String app;
|
||||
final SqliteHelper helper;
|
||||
|
||||
@override State createState() =>
|
||||
new PurchaseSuccessScreenState(val, name, helper, app);
|
||||
}
|
||||
|
||||
class PurchaseSuccessScreenState<T> extends BaseState<PurchaseSuccessScreen> {
|
||||
|
||||
PurchaseSuccessScreenState(String sum, String username, SqliteHelper helper,
|
||||
String app) {
|
||||
this.sum = sum;
|
||||
this.username = username;
|
||||
this.helper = helper;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
String sum, username;
|
||||
int currency;
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.carryingPurchase();
|
||||
}
|
||||
|
||||
@override String getHintString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override Widget build(BuildContext context) {
|
||||
if (currency == null) {
|
||||
helper.getCurrency().then((currency) {
|
||||
setState(() {
|
||||
this.currency = currency;
|
||||
});
|
||||
});
|
||||
}
|
||||
return getMainWidget();
|
||||
}
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(children: <Widget>[
|
||||
getValueWithDescription(StringsLocalization.buyer(), username),
|
||||
getSuccessMessage(),
|
||||
new Expanded(child: new Center()),
|
||||
wrapButton(getScreenMargins(74.0), getScanButton())
|
||||
]);
|
||||
}
|
||||
|
||||
getScreenMargins(double bottom) {
|
||||
double side = 42.0;
|
||||
return new EdgeInsets.only(bottom: bottom, left: side, right: side);
|
||||
}
|
||||
|
||||
getScanButton() {
|
||||
String title = StringsLocalization.scan();
|
||||
return buildRaisedButton(title, () => startScanner(context, app, helper));
|
||||
}
|
||||
|
||||
getSuccessMessage() {
|
||||
return new Row(children: <Widget>[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(getMessageTitle(), textAlign: TextAlign.center,
|
||||
style: new TextStyle(fontWeight: FontWeight.bold,
|
||||
color: tokenActiveTextColor)))))
|
||||
]);
|
||||
}
|
||||
|
||||
getMessageTitle() {
|
||||
if (currency != null) {
|
||||
return StringsLocalization.purchaseCompleted(sum, currency);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +1,41 @@
|
||||
import 'package:checker/finish_registration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:convert'; // Пакет для обработки json с ответом от сервера.
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:checker/base/base_screen.dart';
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/base_state.dart';
|
||||
import 'package:checker/screens/finish_registration.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
// Пакет для обработки json с ответом от сервера.
|
||||
|
||||
|
||||
/// Экран регистрации магазина и кассы.
|
||||
class RegistrationScreen extends StatefulWidget {
|
||||
@override State createState() => new _RegistrationScreenState();
|
||||
class RegistrationScreen extends BaseScreen {
|
||||
|
||||
RegistrationScreen(helper, app) : super(helper, app);
|
||||
|
||||
@override State createState() => new RegistrationScreenState(helper, app);
|
||||
}
|
||||
|
||||
class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
class RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
|
||||
RegistrationScreenState(SqliteHelper helper, String app) {
|
||||
this.helper = helper;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
@override Widget build(BuildContext ctx) {
|
||||
return getMainWidget();
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
return StringsLocalization.registration();
|
||||
}
|
||||
|
||||
@override getHint() {
|
||||
@override getHintString() {
|
||||
return StringsLocalization.idStore();
|
||||
}
|
||||
|
||||
@@ -31,13 +47,14 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
getLogo(),
|
||||
getHintLabel(),
|
||||
getInputField(),
|
||||
getButton()])
|
||||
getButton()
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
@override getTextWidget() {
|
||||
return new TextField(keyboardType: TextInputType.number,
|
||||
decoration: new InputDecoration.collapsed(hintText: getHint(),
|
||||
decoration: new InputDecoration.collapsed(hintText: getHintString(),
|
||||
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)),
|
||||
onChanged: (text) => handleUserInput(text));
|
||||
}
|
||||
@@ -55,8 +72,8 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
|
||||
/// Токен кассы - это DIN код. DIN код - это специальный код динекта, максимальная его длина - 25 символов.
|
||||
_isValidMerchantID() {
|
||||
print("${textFieldValue.length}");
|
||||
return textFieldValue.length > 0 && textFieldValue.length < 25;
|
||||
print("${dinCode.length}");
|
||||
return dinCode.length > 0 && dinCode.length < 25;
|
||||
}
|
||||
|
||||
/// Показать progressBar, запросить токен.
|
||||
@@ -70,21 +87,22 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
/// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена.
|
||||
_register() async {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
createToken(textFieldValue, await platform.invokeMethod('getPosID')).then((response) {
|
||||
|
||||
String posID = await helper.getPosID();
|
||||
String locale = await helper.getLocale();
|
||||
createToken(dinCode, posID, locale).then((response) {
|
||||
setState(() {
|
||||
error = null;
|
||||
loading = false;
|
||||
});
|
||||
|
||||
print(response.body);
|
||||
|
||||
print(response.statusCode.toString());
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
String token = parsedMap['token'];
|
||||
platform.invokeMethod('saveToken', {'token' : token});
|
||||
platform.invokeMethod('saveMerchantID', {'merchantID' : textFieldValue});
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
helper.createSession(dinCode, posID, parsedMap['token']).then((_) {
|
||||
pushRouteReplacement(context, new FinishRegistrationScreen(helper, app));
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
error = parsedMap['errors'][0];
|
||||
131
lib/screens/settings.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
import 'package:checker/base/base_screen.dart';
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/screens/currencies.dart';
|
||||
import 'package:checker/screens/languages.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class SettingsScreen extends BaseScreen {
|
||||
|
||||
final bool returnToScanner;
|
||||
|
||||
SettingsScreen(helper, app, this.returnToScanner) : super(helper, app);
|
||||
|
||||
@override State createState() => new SettingsState(helper, app, returnToScanner);
|
||||
}
|
||||
|
||||
class MenuItem {
|
||||
|
||||
// Заголовок пункта меню и выбранное значение.
|
||||
String title, selectedValue;
|
||||
|
||||
MenuItem(this.title, this.selectedValue);
|
||||
}
|
||||
|
||||
class SettingsState extends BaseState<SettingsScreen> {
|
||||
|
||||
List<MenuItem> menuItems = [
|
||||
new MenuItem('', ''),
|
||||
new MenuItem('', '')
|
||||
];
|
||||
|
||||
bool returnToScanner;
|
||||
|
||||
SettingsState(SqliteHelper helper, String app, bool returnToScanner) {
|
||||
this.helper = helper;
|
||||
this.app = app;
|
||||
this.returnToScanner = returnToScanner;
|
||||
}
|
||||
|
||||
@override Widget build(BuildContext ctx) {
|
||||
helper.getSettings().then((info) {
|
||||
setState(() {
|
||||
menuItems[0].title = StringsLocalization.currency();
|
||||
menuItems[1].title = StringsLocalization.locale();
|
||||
menuItems[0].selectedValue = info['currency'].toString();
|
||||
menuItems[1].selectedValue = info['locale'] == null ? Intl.defaultLocale : info['locale'].toString();
|
||||
});
|
||||
});
|
||||
return new WillPopScope(onWillPop: onWillPop, child: getMainWidget());
|
||||
}
|
||||
|
||||
Widget getMainWidget() {
|
||||
return new Scaffold(appBar: getAppBar(),
|
||||
body: getScreenContent());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getScreenContent() {
|
||||
return new Container(
|
||||
margin: new EdgeInsets.only(top: 16.0),
|
||||
child: new ListView(children: getSettings()));
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> getMenuButtons() {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Widget> getSettings() {
|
||||
List<Widget> widgets = new List();
|
||||
for (int i = 0; i < menuItems.length; i++) {
|
||||
if (menuItems[i].selectedValue != '') {
|
||||
widgets.add(getSettingsItem(() => onPressed(menuItems.indexOf(menuItems[i])),
|
||||
menuItems[i].title,
|
||||
i == 0 ? getCurrencyTitle(int.parse(menuItems[i].selectedValue)) : getLocaleTitle(menuItems[i].selectedValue)));
|
||||
}
|
||||
}
|
||||
return widgets;
|
||||
}
|
||||
|
||||
Widget getSettingsItem(VoidCallback onPressed, String title, String value) {
|
||||
return new Container(
|
||||
height: 56.0,
|
||||
padding: new EdgeInsets.only(left: 8.0),
|
||||
child: (new FlatButton(
|
||||
onPressed: onPressed,
|
||||
child: new Row(children: <Widget>[
|
||||
new Expanded(child: new Text(title, style: new TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: faqGrey,
|
||||
fontSize: 14.0))),
|
||||
new Text(value,
|
||||
style: new TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
color: faqGrey,
|
||||
fontSize: 14.0)),
|
||||
getArrow()
|
||||
]))));
|
||||
}
|
||||
|
||||
void onPressed(int position) {
|
||||
switch (position) {
|
||||
case 0 :
|
||||
return pushRoute(context, new CurrenciesScreen(helper, app));
|
||||
case 1 :
|
||||
return pushRoute(context, new LanguagesScreen(helper, app));
|
||||
}
|
||||
}
|
||||
|
||||
Widget getArrow() {
|
||||
return new Container(margin: new EdgeInsets.only(left: 8.0),
|
||||
child: new Image.asset(settings_arrow_png, height: 42.0));
|
||||
}
|
||||
|
||||
@override
|
||||
String getTitle() {
|
||||
return StringsLocalization.settings();
|
||||
}
|
||||
|
||||
onWillPop() {
|
||||
if(returnToScanner) {
|
||||
return startScanner(context, app, helper);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
220
lib/screens/splash.dart
Normal file
@@ -0,0 +1,220 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:checker/base/base_state.dart';
|
||||
import 'package:checker/common.dart';
|
||||
import 'package:checker/consts.dart';
|
||||
import 'package:checker/db.dart';
|
||||
import 'package:checker/network.dart';
|
||||
import 'package:checker/resources.dart';
|
||||
import 'package:checker/screens/finish_registration.dart';
|
||||
import 'package:checker/screens/registration.dart';
|
||||
import 'package:checker/strings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
@override State createState() => new _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends BaseState<SplashScreen> {
|
||||
|
||||
@override Widget build(BuildContext ctx) {
|
||||
if (helper == null) {
|
||||
helper = new SqliteHelper();
|
||||
helper.open().then((_) {
|
||||
if (app == null) {
|
||||
platform.invokeMethod('getFlavor').then((flavor) {
|
||||
app = flavor;
|
||||
setState(() {
|
||||
onStart();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return getScreenContent();
|
||||
}
|
||||
|
||||
void onStart() {
|
||||
helper.getLocale().then((locale) {
|
||||
if (locale == null) {
|
||||
initWithSystemValue();
|
||||
} else {
|
||||
initWithSavedValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void initWithSystemValue() {
|
||||
platform.invokeMethod('getLocale').then((locale) {
|
||||
helper.getSettings().then((settings) {
|
||||
if (settings == null) {
|
||||
createSettingsTable(locale);
|
||||
} else {
|
||||
initLocale(locale, () {
|
||||
showNext();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void initWithSavedValue() {
|
||||
helper.getLocale().then((locale) {
|
||||
initLocale(locale, () {
|
||||
showNext();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void createSettingsTable(String locale) {
|
||||
platform.invokeMethod('getCurrency').then((currency) {
|
||||
helper.createAppInfo(locale, currency);
|
||||
initLocale(locale, () {
|
||||
showNext();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void initLocale<T>(String locale, Future<T> onValue()) {
|
||||
Intl.defaultLocale = locale;
|
||||
StringsLocalization.load(locale).then((_) {
|
||||
onValue();
|
||||
});
|
||||
}
|
||||
|
||||
void showNext() {
|
||||
new Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
showNextScreen();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getScreenContent() {
|
||||
return app == null
|
||||
? getBackground()
|
||||
: new Stack(
|
||||
children: <Widget>[
|
||||
getBackground(),
|
||||
getLogo(),
|
||||
new Align(
|
||||
alignment: FractionalOffset.bottomRight,
|
||||
child: new Container(
|
||||
margin: new EdgeInsets.only(
|
||||
right: 11.0,
|
||||
bottom: 5.0),
|
||||
child: new Image.asset(powered_by_dinect_splash_png,
|
||||
height: 16.0,
|
||||
width: 122.0)))
|
||||
]);
|
||||
}
|
||||
|
||||
/// Возвращает столбец с логотипом приложения и текстом под ним.
|
||||
/// Столбец занимает не все доступное пространство, а необходимый минимум в центре экрана.
|
||||
getLogo() {
|
||||
return new Center(
|
||||
child: new Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
new Image.asset(
|
||||
Resources.getLogo(app),
|
||||
height: 112.0,
|
||||
width: 252.0),
|
||||
new Image.asset(
|
||||
splash_text_png,
|
||||
height: 40.0,
|
||||
width: 240.0)
|
||||
]));
|
||||
}
|
||||
|
||||
/// Запуск следующего экрана приложения.
|
||||
showNextScreen() async {
|
||||
String token = await helper.getToken();
|
||||
String locale = await helper.getLocale();
|
||||
|
||||
// В случае, если в приложении отсутствует токен,
|
||||
// необходимо запустить регистрацию кассы.
|
||||
if (token == null) {
|
||||
pushRouteReplacement(context, new RegistrationScreen(helper, app));
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
checkTokenStatus(token, locale).then((statusResponse) {
|
||||
handleStatusResponse(statusResponse, helper);
|
||||
}).catchError((error) {
|
||||
handleError(error.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработка ответа.
|
||||
/// В случае, если токен был удален может прийти active: false, либо 404.
|
||||
/// Если токен не активен, попробовать создать его еще раз.
|
||||
handleStatusResponse(var statusResponse, SqliteHelper helper) async {
|
||||
int code = statusResponse.statusCode;
|
||||
|
||||
if (code == 404) {
|
||||
helper.clear().then((result) {
|
||||
pushRouteReplacement(context, new RegistrationScreen(helper, app));
|
||||
});
|
||||
} else {
|
||||
Map status = JSON.decode(statusResponse.body);
|
||||
bool active = status['active'] == null ? false : status['active'];
|
||||
|
||||
if (active) {
|
||||
startScanner(context, app, helper);
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
_createToken(helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправляется запрос на создание токена.
|
||||
///
|
||||
/// Если вернулся код 409, значит такой токен уже существует и активирован.
|
||||
/// Нужно направить пользователя на экран подтверждения активации.
|
||||
///
|
||||
/// Если вернулся код 200, значит токен был ранее удален и только что снова создался.
|
||||
/// Нужно удалить его и направить пользователя на экран регистрации.
|
||||
_createToken(SqliteHelper helper) async {
|
||||
String merchantID = await helper.getMerchantID();
|
||||
String posID = await helper.getPosID();
|
||||
String locale = await helper.getLocale();
|
||||
|
||||
createToken(merchantID, posID, locale).then((response) {
|
||||
if (response.statusCode == 409) {
|
||||
pushRouteReplacement(context, new FinishRegistrationScreen(helper, app));
|
||||
} else if (response.statusCode == 201) {
|
||||
clearToken(response, helper);
|
||||
}
|
||||
}).catchError((error) {
|
||||
handleError(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
/// Очищаем бд, делаем запрос на удаление токена.
|
||||
Future clearToken(Response response, SqliteHelper helper) async {
|
||||
String locale = await helper.getLocale();
|
||||
helper.clear().then((_) {
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
deleteToken(parsedMap['token'], locale).then((_) {
|
||||
Navigator.of(context).pop();
|
||||
pushRouteReplacement(context, new RegistrationScreen(helper, app));
|
||||
}).catchError((error) {
|
||||
handleError(error.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Закрываем соединение, логируем ошибку.
|
||||
void handleError(String error) {
|
||||
helper.close().then((_) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
145
lib/splash.dart
@@ -1,145 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'common.dart';
|
||||
import 'network.dart';
|
||||
import 'consts.dart';
|
||||
import 'registration.dart';
|
||||
import 'finish_registration.dart';
|
||||
import 'resources.dart';
|
||||
import 'base_state.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
@override State createState() => new _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends BaseState<SplashScreen> {
|
||||
|
||||
@override
|
||||
Widget getScreenContent() {
|
||||
return app == null
|
||||
? getBackground()
|
||||
: new Stack(
|
||||
children: <Widget>[
|
||||
getBackground(),
|
||||
getLogo(),
|
||||
new Align(
|
||||
alignment: FractionalOffset.bottomRight,
|
||||
child: new Container(
|
||||
margin: new EdgeInsets.only(
|
||||
right: 11.0,
|
||||
bottom: 5.0),
|
||||
child: new Image.asset(powered_by_dinect_splash_png,
|
||||
height: 16.0,
|
||||
width: 122.0)))]);
|
||||
}
|
||||
|
||||
@override Widget getMainWidget() {
|
||||
return getScreenContent();
|
||||
}
|
||||
|
||||
@override
|
||||
String getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override void onStart() {
|
||||
new Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
showNextScreen(context);
|
||||
});
|
||||
}
|
||||
|
||||
/// Возвращает столбец с логотипом приложения и текстом под ним.
|
||||
/// Столбец занимает не все доступное пространство, а необходимый минимум в центре экрана.
|
||||
getLogo() {
|
||||
return new Center(
|
||||
child: new Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
new Image.asset(
|
||||
Resources.getLogo(app),
|
||||
height: 112.0,
|
||||
width: 252.0),
|
||||
new Image.asset(
|
||||
splash_text_png,
|
||||
height: 40.0,
|
||||
width: 240.0)]));
|
||||
}
|
||||
|
||||
/// Запуск следующего экрана приложения.
|
||||
showNextScreen(BuildContext context) async {
|
||||
|
||||
String token = await platform.invokeMethod('getToken');
|
||||
|
||||
// В случае, если в приложении отсутствует токен,
|
||||
// необходимо запустить регистрацию кассы.
|
||||
if (token == null) {
|
||||
pushRoute(context, new RegistrationScreen());
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
checkTokenStatus(token).then((statusResponse) {
|
||||
handleStatusResponse(context, statusResponse);
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработка ответа.
|
||||
/// В случае, если токен был удален может прийти active: false, либо 404.
|
||||
/// Если токен не активен, попробовать создать его еще раз.
|
||||
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 status = JSON.decode(statusResponse.body);
|
||||
bool active = status['active'] == null ? false : status['active'];
|
||||
|
||||
if (active) {
|
||||
startScanner(context, app);
|
||||
} else {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
_createToken(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправляется запрос на создание токена.
|
||||
///
|
||||
/// Если вернулся код 409, значит такой токен уже существует и активирован.
|
||||
/// Нужно направить пользователя на экран подтверждения активации.
|
||||
///
|
||||
/// Если вернулся код 200, значит токен был ранее удален и только что снова создался.
|
||||
/// Нужно удалить его и направить пользователя на экран регистрации.
|
||||
_createToken(BuildContext ctx) async {
|
||||
String merchantID = await platform.invokeMethod('getMerchantID');
|
||||
String posID = await platform.invokeMethod('getPosID');
|
||||
createToken(merchantID, posID).then((response) {
|
||||
if (response.statusCode == 409) {
|
||||
pushRoute(ctx, new FinishRegistrationScreen());
|
||||
} else if (response.statusCode == 201) {
|
||||
platform.invokeMethod('removeKeys').then((result) {
|
||||
Map parsedMap = JSON.decode(result);
|
||||
deleteToken(parsedMap['token']).then((response) {
|
||||
print(response.body);
|
||||
Navigator.of(ctx).pop(); // Убираем текущий route
|
||||
pushRoute(ctx, new RegistrationScreen()); // Запускаем регистрацию
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
}).catchError((error) => print(error.toString()));
|
||||
}
|
||||
}
|
||||
107
lib/strings.dart
@@ -9,15 +9,78 @@ class StringsLocalization {
|
||||
return initializeMessages(locale);
|
||||
}
|
||||
|
||||
static String confirmPurchase(String val) {
|
||||
return sprintf(Intl.message('confirm_purchase', name: 'confirm_purchase', locale: Intl.defaultLocale), [val]);
|
||||
static String declineCurrency(int num, int code) {
|
||||
|
||||
int residual = num % 100;
|
||||
if (residual >= 20) {
|
||||
residual %= 10;
|
||||
}
|
||||
|
||||
static String purchaseCompleted(String val) {
|
||||
return sprintf(Intl.message('purchase_complite', name: 'purchase_complite', locale: Intl.defaultLocale), [val]);
|
||||
List<String> strings = currencies(code);
|
||||
|
||||
switch (residual) {
|
||||
case 1:
|
||||
return strings[0];
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return strings[1];
|
||||
default: // case 0, 5-19
|
||||
return strings[2];
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> currencies(int code) {
|
||||
|
||||
String nominative, singular, plural;
|
||||
|
||||
switch (code) {
|
||||
case 643:
|
||||
nominative = nominativeRuble();
|
||||
singular = singularRuble();
|
||||
plural = pluralRuble();
|
||||
break;
|
||||
case 840:
|
||||
nominative = nominativeDollar();
|
||||
singular = singularDollar();
|
||||
plural = pluralDollar();
|
||||
break;
|
||||
case 980:
|
||||
nominative = nominativeHryvna();
|
||||
singular = singularHryvna();
|
||||
plural = pluralHryvna();
|
||||
break;
|
||||
case 978:
|
||||
nominative = nominativeEuro();
|
||||
singular = singularEuro();
|
||||
plural = pluralEuro();
|
||||
break;
|
||||
case 398:
|
||||
nominative = nominativeTenge();
|
||||
singular = singularTenge();
|
||||
plural = pluralTenge();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return [nominative, singular, plural];
|
||||
}
|
||||
|
||||
|
||||
static String confirmPurchase(String val, int code) {
|
||||
String trimmedVal =val.substring(0, val.length - 3);
|
||||
return sprintf(Intl.message('confirm_purchase', name: 'confirm_purchase', locale: Intl.defaultLocale), [trimmedVal, declineCurrency(int.parse(trimmedVal), code)]);
|
||||
}
|
||||
|
||||
static String purchaseCompleted(String val, int code) {
|
||||
String trimmedVal =val.substring(0, val.length - 3);
|
||||
return sprintf(Intl.message('purchase_complite', name: 'purchase_complite', locale: Intl.defaultLocale), [val, declineCurrency(int.parse(trimmedVal), code)]);
|
||||
}
|
||||
|
||||
static String registration() => Intl.message('registration', name: 'registration', locale: Intl.defaultLocale);
|
||||
static String usage() => Intl.message('usage', name: 'usage', locale: Intl.defaultLocale);
|
||||
static String support() => Intl.message('support', name: 'support', locale: Intl.defaultLocale);
|
||||
static String common() => Intl.message('common', name: 'common', locale: Intl.defaultLocale);
|
||||
static String idStore() => Intl.message('ID_Store', name: 'ID_Store', locale: Intl.defaultLocale);
|
||||
static String signUp() => Intl.message('sign_up', name: 'sign_up', locale: Intl.defaultLocale);
|
||||
static String specifyDinStore() => Intl.message('specify_din_store', name: 'specify_din_store', locale: Intl.defaultLocale);
|
||||
@@ -30,7 +93,6 @@ class StringsLocalization {
|
||||
static String appActivated() => Intl.message('app_activ', name: 'app_activ', locale: Intl.defaultLocale);
|
||||
static String completeRegistration() => Intl.message('complite_activ', name: 'complite_activ', locale: Intl.defaultLocale);
|
||||
static String cardScanner() => Intl.message('card_scaner', name: 'card_scaner', locale: Intl.defaultLocale);
|
||||
static String userName() => Intl.message('user_name', name: 'user_name', locale: Intl.defaultLocale);
|
||||
static String card() => Intl.message('card', name: 'card', locale: Intl.defaultLocale);
|
||||
static String reward() => Intl.message('reward', name: 'reward', locale: Intl.defaultLocale);
|
||||
static String sum() => Intl.message('sum', name: 'sum', locale: Intl.defaultLocale);
|
||||
@@ -39,4 +101,39 @@ class StringsLocalization {
|
||||
static String scan() => Intl.message('scan', name: 'scan', locale: Intl.defaultLocale);
|
||||
static String buyer() => Intl.message('buyer', name: 'buyer', locale: Intl.defaultLocale);
|
||||
static String idNotFound() => Intl.message('ID_not_found', name: 'ID_not_found', locale: Intl.defaultLocale);
|
||||
static String settings() => Intl.message('settings', name: 'settings', locale: Intl.defaultLocale);
|
||||
static String help() => Intl.message('help', name: 'help', locale: Intl.defaultLocale);
|
||||
static String logout() => Intl.message('logout', name: 'logout', locale: Intl.defaultLocale);
|
||||
static String currency() => Intl.message('currency', name: 'currency', locale: Intl.defaultLocale);
|
||||
static String locale() => Intl.message('locale', name: 'locale', locale: Intl.defaultLocale);
|
||||
|
||||
// Валюты
|
||||
static String nominativeRuble() => Intl.message('nominative_ruble', name: 'nominative_ruble', locale: Intl.defaultLocale);
|
||||
static String singularRuble() => Intl.message('singular_ruble', name: 'singular_ruble', locale: Intl.defaultLocale);
|
||||
static String pluralRuble() => Intl.message('plural_ruble', name: 'plural_ruble', locale: Intl.defaultLocale);
|
||||
|
||||
static String nominativeEuro() => Intl.message('nominative_euro', name: 'nominative_euro', locale: Intl.defaultLocale);
|
||||
static String singularEuro() => Intl.message('singular_euro', name: 'singular_euro', locale: Intl.defaultLocale);
|
||||
static String pluralEuro() => Intl.message('plural_euro', name: 'plural_euro', locale: Intl.defaultLocale);
|
||||
|
||||
|
||||
static String nominativeDollar() => Intl.message('nominative_dollar', name: 'nominative_dollar', locale: Intl.defaultLocale);
|
||||
static String singularDollar() => Intl.message('singular_dollar', name: 'singular_dollar', locale: Intl.defaultLocale);
|
||||
static String pluralDollar() => Intl.message('plural_dollar', name: 'plural_dollar', locale: Intl.defaultLocale);
|
||||
|
||||
static String nominativeHryvna() => Intl.message('nominative_hryvna', name: 'nominative_hryvna', locale: Intl.defaultLocale);
|
||||
static String singularHryvna() => Intl.message('singular_hryvna', name: 'singular_hryvna', locale: Intl.defaultLocale);
|
||||
static String pluralHryvna() => Intl.message('plural_hryvna', name: 'plural_hryvna', locale: Intl.defaultLocale);
|
||||
|
||||
static String nominativeTenge() => Intl.message('nominative_tenge', name: 'nominative_tenge', locale: Intl.defaultLocale);
|
||||
static String singularTenge() => Intl.message('singular_tenge', name: 'singular_tenge', locale: Intl.defaultLocale);
|
||||
static String pluralTenge() => Intl.message('plural_tenge', name: 'plural_tenge', locale: Intl.defaultLocale);
|
||||
|
||||
static String registrationGuide() => Intl.message('registration_guide', name: 'registration_guide', locale: Intl.defaultLocale);
|
||||
static String usageGuide() => Intl.message('usage_guide', name: 'usage_guide', locale: Intl.defaultLocale);
|
||||
static String commonGuide() => Intl.message('common_guide', name: 'common_guide', locale: Intl.defaultLocale);
|
||||
|
||||
static String supportGuide(String phone, String url) {
|
||||
return sprintf(Intl.message('support_guide', name: 'support_guide', locale: Intl.defaultLocale), [phone, url]);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ dependencies:
|
||||
intl: '>=0.14.0 <0.16.0'
|
||||
intl_translation: '>=0.14.0 <0.16.0'
|
||||
sprintf: "^3.0.2"
|
||||
path_provider: "^0.2.1+1"
|
||||
sqflite: any
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
@@ -30,10 +32,14 @@ flutter:
|
||||
- assets/autobonus_splash.png
|
||||
- assets/pip_splash.png
|
||||
|
||||
- assets/settings.png
|
||||
- assets/settings_arrow.png
|
||||
- assets/help.png
|
||||
- assets/check.png
|
||||
- assets/logout.png
|
||||
- assets/activate_token_message_background.png
|
||||
- assets/active_token_message_background.png
|
||||
- assets/expansion_icon.png
|
||||
- assets/faq_expansion_icon.png
|
||||
- assets/powered_by_dinect_splash.png
|
||||
- assets/powered_by_dinect.png
|
||||
- assets/autobonus_logo.png
|
||||
|
||||