Экран сканера

This commit is contained in:
Ivan Murashov
2017-07-19 18:59:04 +03:00
parent ff8ddf4334
commit bb45d252f1
31 changed files with 223 additions and 127 deletions

View File

@@ -15,8 +15,8 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 21 compileSdkVersion 25
buildToolsVersion '21.0.0' buildToolsVersion '25.0.0'
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'
@@ -48,7 +48,5 @@ flutter {
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.google.android.gms:play-services-gcm:11.0.1" compile "com.google.android.gms:play-services-gcm:11.0.1"
androidTestCompile 'com.android.support:support-annotations:21.0.0' compile 'com.android.support:appcompat-v7:25.0.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
} }

View File

@@ -17,7 +17,7 @@
In most cases you can leave this as-is, but you if you want to provide 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 additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. --> FlutterApplication and put your custom class here. -->
<application android:name="io.flutter.app.FlutterApplication" android:label="checker" android:icon="@mipmap/ic_launcher"> <application android:name="io.flutter.app.FlutterApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher_app">
<activity android:name="com.dinect.checker.activity.MainActivity" <activity android:name="com.dinect.checker.activity.MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar" android:theme="@android:style/Theme.Black.NoTitleBar"
@@ -31,7 +31,7 @@
</activity> </activity>
<activity android:name="com.dinect.checker.activity.CameraActivity" <activity android:name="com.dinect.checker.activity.CameraActivity"
android:theme="@android:style/Theme.Black.NoTitleBar"/> android:theme="@style/AppTheme"/>
<service <service
android:name="com.dinect.checker.service.RegistrationIntentService" android:name="com.dinect.checker.service.RegistrationIntentService"

View File

@@ -1,46 +1,53 @@
package com.dinect.checker.activity; package com.dinect.checker.activity;
import android.app.Activity; import android.support.v7.app.AppCompatActivity;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.util.DisplayMetrics;
import android.content.res.Resources ;
import android.view.View; import android.view.View;
import android.view.Menu;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.Window; import android.view.Window;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.Button; import android.widget.Button;
import android.graphics.Rect;
import android.support.v7.widget.Toolbar;
import android.hardware.Camera; import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback; import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters; import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size; import android.hardware.Camera.Size;
import android.support.v7.app.ActionBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import net.sourceforge.zbar.ImageScanner; import net.sourceforge.zbar.ImageScanner;
import net.sourceforge.zbar.Image; import net.sourceforge.zbar.Image;
import net.sourceforge.zbar.Symbol; import net.sourceforge.zbar.Symbol;
import net.sourceforge.zbar.SymbolSet; import net.sourceforge.zbar.SymbolSet;
import net.sourceforge.zbar.Config; import net.sourceforge.zbar.Config;
import android.graphics.YuvImage;
import com.dinect.checker.R; import com.dinect.checker.R;
import com.dinect.checker.view.CameraPreview;
public class CameraActivity extends Activity { public class CameraActivity extends AppCompatActivity {
private Camera mCamera; private Camera mCamera;
private SurfaceView mPreview; private Handler mAutoFocusHandler;
private Handler autoFocusHandler; private ImageScanner mScanner;
private HashMap<String, Integer> mContours = new HashMap<>();
ImageScanner scanner; private boolean mBarcodeScanned = false;
private boolean barcodeScanned = false;
private boolean previewing = true; private boolean previewing = true;
static { static {
@@ -49,28 +56,37 @@ public class CameraActivity extends Activity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.a_scanner); setContentView(R.layout.a_scanner);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
autoFocusHandler = new Handler(); mAutoFocusHandler = new Handler();
mCamera = getCameraInstance(); mCamera = getCameraInstance();
mScanner = new ImageScanner();
/* Instance barcode scanner */ SurfaceView preview = (SurfaceView) findViewById(R.id.cameraPreview);
scanner = new ImageScanner(); SurfaceHolder holder = preview.getHolder();
scanner.setConfig(0, Config.X_DENSITY, 3);
scanner.setConfig(0, Config.Y_DENSITY, 3);
mPreview = (SurfaceView) findViewById(R.id.cameraPreview); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(getString(R.string.scanner_title));
actionBar.setDisplayHomeAsUpEnabled(true);
}
SurfaceHolder holder = mPreview.getHolder();
holder.addCallback(new SurfaceHolder.Callback() { holder.addCallback(new SurfaceHolder.Callback() {
@Override @Override
public void surfaceCreated(SurfaceHolder holder) { public void surfaceCreated(SurfaceHolder holder) {
try { try {
mCamera.setPreviewDisplay(holder); mCamera.setPreviewDisplay(holder);
mCamera.startPreview(); mCamera.startPreview();
mCamera.setPreviewCallback(previewCallback); mCamera.setPreviewCallback(previewCallback);
mCamera.autoFocus(autoFocusCB); mCamera.autoFocus(autoFocusCB);
mCamera.setDisplayOrientation(90);
initCountours();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -78,7 +94,6 @@ public class CameraActivity extends Activity {
@Override @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mCamera.setDisplayOrientation(270);
} }
@Override @Override
@@ -88,6 +103,31 @@ public class CameraActivity extends Activity {
}); });
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
private void initCountours() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
Resources res = getResources();
mContours.put("left", (int) res.getDimension(R.dimen.scanner_contour_top));
mContours.put("top", (int) res.getDimension(R.dimen.scanner_contour_left));
mContours.put("width", (int) res.getDimension(R.dimen.scanner_contour_height));
mContours.put("height", width - (2 * (int) res.getDimension(R.dimen.scanner_contour_left)));
Log.d("kifio", "left: " + mContours.get("left"));
Log.d("kifio", "top: " + mContours.get("top"));
Log.d("kifio", "width: " + mContours.get("width"));
Log.d("kifio", "height: " + mContours.get("height"));
}
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
releaseCamera(); releaseCamera();
@@ -121,22 +161,24 @@ public class CameraActivity extends Activity {
PreviewCallback previewCallback = new PreviewCallback() { PreviewCallback previewCallback = new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) { public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Parameters parameters = camera.getParameters();
Size size = parameters.getPreviewSize(); Size size = parameters.getPreviewSize();
Image barcode = new Image(size.width, size.height, "Y800"); Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data); barcode.setData(data);
// barcode.setCrop(mContours.get("left"), mContours.get("top"), mContours.get("width"), mContours.get("height"));
int result = scanner.scanImage(barcode); int result = mScanner.scanImage(barcode);
if (result != 0) { if (result != 0) {
previewing = false; previewing = false;
mCamera.setPreviewCallback(null); mCamera.setPreviewCallback(null);
mCamera.stopPreview(); mCamera.stopPreview();
SymbolSet syms = scanner.getResults(); SymbolSet syms = mScanner.getResults();
for (Symbol sym : syms) { for (Symbol sym : syms) {
barcodeScanned = true; mBarcodeScanned = true;
Toast.makeText(CameraActivity.this, sym.getData(), Toast.LENGTH_SHORT).show(); Toast.makeText(CameraActivity.this, sym.getData(), Toast.LENGTH_SHORT).show();
} }
} }
@@ -146,7 +188,7 @@ public class CameraActivity extends Activity {
// Mimic continuous auto-focusing // Mimic continuous auto-focusing
AutoFocusCallback autoFocusCB = new AutoFocusCallback() { AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) { public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000); mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
} }
}; };
} }

View File

@@ -1,85 +0,0 @@
package com.dinect.checker.view;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private PreviewCallback previewCallback;
private AutoFocusCallback autoFocusCallback;
public CameraPreview(Context context, Camera camera,
PreviewCallback previewCb,
AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Camera preview released in activity
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
/*
* If your preview can change or rotate, take care of those events here.
* Make sure to stop the preview before resizing or reformatting it.
*/
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
try {
// Hard code camera surface rotation 90 degs to match Activity view in portrait
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e){
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true"><shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp"/>
<solid android:color="#3078c0"/>
</shape>
</item>
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp"/>
<solid android:color="#4272e7"/>
</shape>
</item>
</selector>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient android:type="linear"
android:angle="90"
android:startColor="#00000000"
android:endColor="#36000000"/>
</shape>

View File

@@ -2,7 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout" android:id="@+id/rootLayout"
android:background="@android:color/white" android:background="#404040"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -10,5 +10,89 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
<View android:layout_height="178dp"
android:layout_width="match_parent"
android:background="#404040"/>
<!--<View android:layout_width="match_parent"-->
<!--android:layout_height="6dp"-->
<!--android:layout_marginTop="178dp"-->
<!--android:background="#c0000000"/>-->
<!--<View android:layout_width="24dp"-->
<!--android:layout_height="232dp"-->
<!--android:layout_marginTop="184dp"-->
<!--android:layout_gravity="left"-->
<!--android:background="#c0000000"/>-->
<!--<View android:layout_width="24dp"-->
<!--android:layout_height="232dp"-->
<!--android:layout_marginTop="184dp"-->
<!--android:layout_gravity="right"-->
<!--android:background="#c0000000"/>-->
<!--<View android:layout_width="match_parent"-->
<!--android:layout_height="18dp"-->
<!--android:layout_marginTop="416dp"-->
<!--android:background="#c0000000"/>-->
<View android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_marginTop="434dp"
android:background="#404040"/>
<!--<FrameLayout android:id="@+id/scanner_contour"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="200dp"-->
<!--android:layout_marginTop="196dp"-->
<!--android:layout_marginLeft="36dp"-->
<!--android:layout_marginRight="36dp">-->
<!--<View android:layout_width="20dp"-->
<!--android:layout_height="20dp"-->
<!--android:layout_gravity="top|left"-->
<!--android:background="@drawable/shape_top_left"/>-->
<!--<View android:layout_width="20dp"-->
<!--android:layout_height="20dp"-->
<!--android:layout_gravity="top|right"-->
<!--android:background="@drawable/shape_top_right"/>-->
<!--<View android:layout_width="20dp"-->
<!--android:layout_height="20dp"-->
<!--android:layout_gravity="bottom|left"-->
<!--android:background="@drawable/shape_bottom_left"/>-->
<!--<View android:layout_width="20dp"-->
<!--android:layout_height="20dp"-->
<!--android:layout_gravity="bottom|right"-->
<!--android:background="@drawable/shape_bottom_right"/>-->
<!--</FrameLayout>-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#4272e7"
app:titleTextColor="@android:color/white"/>
<View android:id="@+id/toolbarShadow"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_marginTop="?attr/actionBarSize"
android:background="@drawable/shadow_bottom"/>
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:paddingLeft="60dp"
android:paddingRight="60dp"
android:text="@string/scan"
android:textAllCaps="true"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="46dp"
android:background="@drawable/button_blue"/>
</FrameLayout> </FrameLayout>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/faq"
android:orderInCategory="0"
android:title="@string/faq"
android:icon="@drawable/help_outline"
app:showAsAction="ifRoom"/>
<item android:id="@+id/logout"
android:orderInCategory="1"
android:title="@string/logout"
android:icon="@drawable/logout"
app:showAsAction="ifRoom"/>
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -0,0 +1,5 @@
<resources>
<dimen name="scanner_contour_left">24dp</dimen>
<dimen name="scanner_contour_top">178dp</dimen>
<dimen name="scanner_contour_height">232dp</dimen>
</resources>

View File

@@ -0,0 +1,7 @@
<resources>
<string name="app_name">Checker</string>
<string name="scanner_title">Сканнер карты</string>
<string name="scan">Сканировать</string>
<string name="faq">FAQ</string>
<string name="logout">Выход</string>
</resources>

View File

@@ -0,0 +1,6 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar"/>
</resources>

View File

@@ -15,7 +15,7 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
AppBar _getAppBar() { AppBar _getAppBar() {
return new AppBar(title: new Text("Регистрация магазина"), return new AppBar(title: new Text("Регистрация магазина"),
backgroundColor: const Color(0xff4272e7), actions: <Widget>[ backgroundColor: const Color(primaryColor), actions: <Widget>[
new IconButton( new IconButton(
icon: new Icon(Icons.help_outline), icon: new Icon(Icons.help_outline),
tooltip: 'Air it', tooltip: 'Air it',

View File

@@ -21,6 +21,9 @@ const String splash_png = 'assets/splash.png';
const String logout_png = 'assets/logout.png'; const String logout_png = 'assets/logout.png';
const String activate_token_bg_png = 'assets/activate_token_message_background.png'; const String activate_token_bg_png = 'assets/activate_token_message_background.png';
// Colors
const int primaryColor = 0xffeb0004;
final httpClient = createHttpClient(); final httpClient = createHttpClient();
void main() { void main() {
@@ -64,7 +67,7 @@ startScanner() async{
/// Навигация по приложению. /// Навигация по приложению.
/// widget - следующий экран приложения. /// widget - следующий экран приложения.
pushRoute(BuildContext context, Widget widget) { pushRoute(BuildContext context, Widget widget) {
Navigator.of(context).push(new MaterialPageRoute<Null>( Navigator.of(context).pushReplacement(new MaterialPageRoute<Null>(
builder: (BuildContext context) { builder: (BuildContext context) {
return widget; return widget;
})); }));

View File

@@ -19,17 +19,12 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
} }
AppBar _getAppBar() { AppBar _getAppBar() {
return new AppBar(title: new Text("Регистрация магазина"), return new AppBar(title: new Text("Регистрация"),
backgroundColor: const Color(0xff4272e7), actions: <Widget>[ backgroundColor: const Color(primaryColor), actions: <Widget>[
new IconButton( new IconButton(
icon: new Icon(Icons.help_outline), icon: new Icon(Icons.help_outline),
tooltip: 'Air it', tooltip: 'Air it',
onPressed: faq, onPressed: faq,
),
new IconButton(
icon: new Image(height: 24.0, width: 24.0, image: new AssetImage('assets/logout.png')),
tooltip: 'Restitch it',
onPressed: logout,
) )
]); ]);
} }

View File

@@ -16,7 +16,7 @@ class SplashScreen extends StatelessWidget {
_showNextScreen(context); _showNextScreen(context);
}); });
return new Image.asset(logo_png, fit: BoxFit.cover); return new Image.asset(splash_png, fit: BoxFit.cover);
} }
/// Запуск следующего экрана приложения. /// Запуск следующего экрана приложения.