Compare commits

..
8 Commits
Author SHA1 Message Date
lijia 4d0c8d406a 删除 2023-09-11 15:53:39 +08:00
lijia 68d411e544 Merge branch 'master' into ocrcamera 2023-09-11 15:32:25 +08:00
lijia 3955131756 修复百度定位不准确,添加六分钟卸货限制,gradle优化 2023-09-11 15:18:22 +08:00
lijia f4d2048d81 跳转运单详情添加1.5秒延时 2023-08-31 16:45:31 +08:00
lijia 44809df2cf 奔溃问题处理 2023-08-31 16:42:24 +08:00
lijia ce9a03b4d6 百度ocr识别依赖添加 2023-08-30 16:10:33 +08:00
lijia 244a3f22eb 安联测试环境调通 2023-08-30 16:01:52 +08:00
lijia 3505a9caa1 ocr测试 2023-08-30 15:56:18 +08:00
80 changed files with 5214 additions and 253 deletions
+30 -17
View File
@@ -16,19 +16,21 @@ android {
keyPassword '123456' keyPassword '123456'
storeFile file('key.jks') storeFile file('key.jks')
storePassword '123456' storePassword '123456'
v1SigningEnabled true
v2SigningEnabled true
} }
debug { // debug {
keyAlias 'key0' // keyAlias 'key0'
keyPassword '123456' // keyPassword '123456'
storeFile file('key.jks') // storeFile file('key.jks')
storePassword '123456' // storePassword '123456'
} // }
release { // release {
keyAlias 'key0' // keyAlias 'key0'
keyPassword '123456' // keyPassword '123456'
storeFile file('key.jks') // storeFile file('key.jks')
storePassword '123456' // storePassword '123456'
} // }
} }
defaultConfig { defaultConfig {
@@ -50,7 +52,7 @@ android {
ndk { ndk {
//选择要添加的对应 cpu 类型的 .so 库。 //选择要添加的对应 cpu 类型的 .so 库。
abiFilters 'arm64-v8a' abiFilters 'armeabi-v7a', 'arm64-v8a'
// 还可以添加 'x86', 'x86_64', 'mips', 'mips64' , 'arm64-v8a' // 还可以添加 'x86', 'x86_64', 'mips', 'mips64' , 'arm64-v8a'
} }
@@ -86,9 +88,10 @@ android {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
} }
debug { debug {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.config
} }
} }
compileOptions { compileOptions {
@@ -113,15 +116,19 @@ android {
productFlavors { productFlavors {
qa { qa {
manifestPlaceholders = [CHANNEL_VALUE: "好运司机测试"] manifestPlaceholders = [CHANNEL_VALUE: "好运司机测试"]
buildConfigField("String", "OPEN_AL_URL", "\"https://oapi-staging.alct56.com/api/v1/openapi/\"") buildConfigField("String", "OPEN_AL_URL", "\"https://oapi-staging.alct56.com\"")
buildConfigField("String", "BASE_URL", "\"http://app.test.dahehuoyun.com/api/\"") buildConfigField("String", "BASE_URL", "\"http://app.test.dahehuoyun.com/api/\"")
buildConfigField "boolean", "isTest", "true" buildConfigField "boolean", "isTest", "true"
//APP名称,可以在androidMainfest中引用
resValue "string", "appName", "司机测试"
} }
product { product {
manifestPlaceholders = [CHANNEL_VALUE: "大河好运司机"] manifestPlaceholders = [CHANNEL_VALUE: "大河好运司机"]
buildConfigField("String", "OPEN_AL_URL", "\"https://oapi.alct56.com\"") buildConfigField("String", "OPEN_AL_URL", "\"https://oapi.alct56.com\"")
buildConfigField("String", "BASE_URL", "\"http://app.dahehuoyun.com/api/\"") buildConfigField("String", "BASE_URL", "\"http://app.dahehuoyun.com/api/\"")
buildConfigField "boolean", "isTest", "false" buildConfigField "boolean", "isTest", "false"
//APP名称,可以在androidMainfest中引用
resValue "string", "appName", "大河好运司机"
} }
} }
@@ -133,9 +140,14 @@ android {
android.applicationVariants.all { variant -> android.applicationVariants.all { variant ->
variant.outputs.all { variant.outputs.all {
def date = new Date().format("MMdd_HH-mm" , TimeZone.getTimeZone("GMT+08")) def date = new Date().format("MMdd_HH-mm" , TimeZone.getTimeZone("GMT+08"))
if(variant.buildType.name.equals('release')){ def isText = variant.productFlavors[0].properties.get("buildConfigFields").getAt("isTest").properties.get("value")
outputFileName = "dhhy-driver-"+date+"-${versionName}.apk" def appName = null
if (isText=="true") {
appName = '大河好运司机测试_'
}else{
appName = '大河好运司机_'
} }
outputFileName =appName+"${variant.buildType.name}_${defaultConfig.versionName}_${date}.apk"
} }
} }
lintOptions { lintOptions {
@@ -221,5 +233,6 @@ dependencies {
implementation 'com.github.getActivity:XToast:8.2' implementation 'com.github.getActivity:XToast:8.2'
implementation project(path: ':mylibrary') implementation project(path: ':mylibrary')
implementation project(path: ':ocr_ui')
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -54,13 +54,13 @@
android:allowBackup="true" android:allowBackup="true"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:icon="@drawable/diver_logo" android:icon="@drawable/diver_logo"
android:label="@string/app_name" android:label="@string/appName"
android:largeHeap="true" android:largeHeap="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:replace="android:icon"> tools:replace="android:label,android:allowBackup">
<uses-library <uses-library
android:name="com/alct/mdp" android:name="com/alct/mdp"
@@ -312,16 +312,19 @@ public class H5Activity extends AppCompatActivity {
} else { } else {
String tsignCode = uri.getQueryParameter("tsignCode"); String tsignCode = uri.getQueryParameter("tsignCode");
if ("0".equals(tsignCode)) { if ("0".equals(tsignCode)) {
//延时1.5秒跳转运单详情页面
new Handler().postDelayed(() -> {
FaceEvent he = new FaceEvent("刷脸成功"); FaceEvent he = new FaceEvent("刷脸成功");
EventBus.getDefault().post(he); EventBus.getDefault().post(he);
finish(); finish();
}, 800);
tsignCode = "签署成功"; tsignCode = "签署成功";
} else { } else {
tsignCode = "签署失败"; tsignCode = "签署失败";
} }
Toast.makeText(H5Activity.this, "签署结果: " + tsignCode, Toast.LENGTH_LONG).show(); Toast.makeText(H5Activity.this, "签署结果: " + tsignCode, Toast.LENGTH_LONG).show();
} }
finish(); // finish();
return true; return true;
} else if (uri.getScheme().equals("alipays")) { } else if (uri.getScheme().equals("alipays")) {
// 跳转到支付宝刷脸 // 跳转到支付宝刷脸
@@ -1,13 +1,10 @@
package com.arpa.hndahesudintocctmsdriver.cuspop package com.arpa.hndahesudintocctmsdriver.cuspop
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.view.View import android.view.View
import com.arpa.hndahesudintocctmsdriver.R import com.arpa.hndahesudintocctmsdriver.R
import com.dahe.mylibrary.utils.BaseUtils
import com.dahe.mylibrary.utils.ToastUtils
import com.lxj.xpopup.core.CenterPopupView import com.lxj.xpopup.core.CenterPopupView
import kotlinx.android.synthetic.main.load.view.* import kotlinx.android.synthetic.main.load.view.*
import kotlinx.android.synthetic.main.pop_message_center.view.* import kotlinx.android.synthetic.main.pop_message_center.view.*
@@ -52,11 +49,10 @@ class MessagePop(context: Context) : CenterPopupView(context), View.OnClickListe
R.id.tvOk,R.id.tvPhone -> { R.id.tvOk,R.id.tvPhone -> {
dismiss() dismiss()
val toString = tvPhone.text.toString() val toString = tvPhone.text.toString()
val intent = Intent(Intent.ACTION_CALL) val intent = Intent(Intent.ACTION_DIAL)
val data = Uri.parse("tel:$toString") val data = Uri.parse("tel:$toString")
intent.data = data intent.data = data
context.startActivity(intent) context.startActivity(intent)
dismiss()
// BaseUtils.callPhone(content as Activity, tvPhone.text.toString()) // BaseUtils.callPhone(content as Activity, tvPhone.text.toString())
} }
} }
@@ -35,9 +35,9 @@ public class ALProcess {
//企业代码-测试 //企业代码-测试
private static final String ENTERPRISE_CODE_TEXT="E0019093"; private static final String ENTERPRISE_CODE_TEXT="E0019093";
//企业app的身份码-测试 //企业app的身份码-测试
private static final String APP_IDENTITY_TEXT="07a6256cad5642138ed6e962a5a157be"; private static final String APP_IDENTITY_TEXT="a15b4ba5bd2542cc9a6bea9bdadf9ef9";
//企业app验证码-测试 //企业app验证码-测试
private static final String APP_KEY_TEXT="a15b4ba5bd2542cc9a6bea9bdadf9ef9"; private static final String APP_KEY_TEXT="07a6256cad5642138ed6e962a5a157be";
public static void verification(Context con){ public static void verification(Context con){
@@ -141,6 +141,7 @@ public class ALProcess {
MDPLocationCollectionManager.register(con, idy, new OnResultListener() { MDPLocationCollectionManager.register(con, idy, new OnResultListener() {
@Override @Override
public void onSuccess() { public void onSuccess() {
MsgUtil.addHdMsgWatBody(hd, 1111,"认证成功");
Log.e("--success--","验证成功"); Log.e("--success--","验证成功");
getFq(con); getFq(con);
} }
@@ -47,6 +47,7 @@ import com.arpa.hndahesudintocctmsdriver.ui.auth.CertificatesActivity;
import com.arpa.hndahesudintocctmsdriver.ui.news.NewActivity; import com.arpa.hndahesudintocctmsdriver.ui.news.NewActivity;
import com.arpa.hndahesudintocctmsdriver.util.MessageUtils; import com.arpa.hndahesudintocctmsdriver.util.MessageUtils;
import com.arpa.hndahesudintocctmsdriver.util.string.StringUtil; import com.arpa.hndahesudintocctmsdriver.util.string.StringUtil;
import com.baidu.ocr.ui.camera.CameraActivity;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.gyf.cactus.Cactus; import com.gyf.cactus.Cactus;
@@ -94,6 +95,7 @@ import com.youth.banner.loader.ImageLoader;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -336,13 +338,20 @@ public class HomeFragment extends BaseFragment {
} }
}); });
news_btn.setOnClickListener(v -> { news_btn.setOnClickListener(v -> {
if (UiAuxiliary.isLogin(con)) { // TODO: 2023/8/30 测试orc拍照
Intent in = new Intent(con, NewActivity.class); Intent intent = new Intent(getActivity(), CameraActivity.class);
startActivity(in); intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH,
} else { new File(getActivity().getFilesDir(), "pic.jpg").getAbsolutePath());
Intent in = new Intent(con, LoginActivity.class); intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, CameraActivity.CONTENT_TYPE_ID_CARD_FRONT);
startActivity(in); startActivityForResult(intent, 1111);
}
// if (UiAuxiliary.isLogin(con)) {
// Intent in = new Intent(con, NewActivity.class);
// startActivity(in);
// } else {
// Intent in = new Intent(con, LoginActivity.class);
// startActivity(in);
// }
}); });
//Intent serviceIn=new Intent(con, LocationService.class); //Intent serviceIn=new Intent(con, LocationService.class);
//getActivity().startService(serviceIn); //getActivity().startService(serviceIn);
@@ -40,7 +40,10 @@ import com.amap.api.services.route.DriveStep;
import com.amap.api.services.route.RideRouteResult; import com.amap.api.services.route.RideRouteResult;
import com.amap.api.services.route.RouteSearch; import com.amap.api.services.route.RouteSearch;
import com.amap.api.services.route.WalkRouteResult; import com.amap.api.services.route.WalkRouteResult;
import com.arpa.hndahesudintocctmsdriver.util.alert.ToastUtil;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.dahe.mylibrary.utils.TimeUtil;
import com.dahe.mylibrary.utils.ToastUtils;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.arpa.hndahesudintocctmsdriver.R; import com.arpa.hndahesudintocctmsdriver.R;
@@ -135,7 +138,8 @@ public class StartYunDanActivity extends BaseActivity {
break; break;
case 15: case 15:
break; break;
default:break; default:
break;
} }
} }
@@ -203,7 +207,8 @@ public class StartYunDanActivity extends BaseActivity {
Glide.with(con).load(ub.getData().getHeadportraitUrl()).into(head_img); Glide.with(con).load(ub.getData().getHeadportraitUrl()).into(head_img);
} }
break; break;
default: break; default:
break;
} }
}); });
} }
@@ -359,16 +364,12 @@ public class StartYunDanActivity extends BaseActivity {
Toast.makeText(con, "您还没有装货", Toast.LENGTH_SHORT).show(); Toast.makeText(con, "您还没有装货", Toast.LENGTH_SHORT).show();
} }
} else if (sdw.getStatus() == 1) {//装货回单 } else if (sdw.getStatus() == 1) {//装货回单
if(type==0){
Intent in = new Intent(con, ShangChuangImgActivity.class); Intent in = new Intent(con, ShangChuangImgActivity.class);
in.putExtra("type", 2); in.putExtra("type", 2);
in.putExtra("wid", wid); in.putExtra("wid", wid);
in.putExtra("h_type", type); in.putExtra("h_type", type);
in.putExtra("sob", gson.toJson(sob)); in.putExtra("sob", gson.toJson(sob));
startActivity(in); startActivity(in);
}else{
Toast.makeText(con,"您还没有上传回单信息",Toast.LENGTH_SHORT).show();
}
} else if (sdw.getStatus() == 2) {//上传收货单(卸货回单) } else if (sdw.getStatus() == 2) {//上传收货单(卸货回单)
Intent in = new Intent(con, ShangChuangImgActivity.class); Intent in = new Intent(con, ShangChuangImgActivity.class);
in.putExtra("type", 2); in.putExtra("type", 2);
@@ -382,12 +383,30 @@ public class StartYunDanActivity extends BaseActivity {
in.putExtra("sdwStr", gson.toJson(sdw)); in.putExtra("sdwStr", gson.toJson(sdw));
startActivity(in); startActivity(in);
} else {//卸货照片 } else {//卸货照片
StartOrderBean.DataDTO.WayChildrenDTO zInfo;
for (int i = 0; i < sob.getData().getWayChildren().size(); i++) {
if (sob.getData().getWayChildren().get(i).getType() == 1) {
zInfo = sob.getData().getWayChildren().get(i);
int dis = TimeUtil.compareNowDate(TimeUtil.string2Millis(zInfo.getImageTakenDate(), TimeUtil.DEFAULT_FORMAT4));
if (dis > 5) {
if (zInfo.getReceiptUrl() != null && zInfo.getReceiptUrl().size() > 0) {
Intent in = new Intent(con, ShangChuangImgActivity.class); Intent in = new Intent(con, ShangChuangImgActivity.class);
in.putExtra("type", type); in.putExtra("type", type);
in.putExtra("wid", wid); in.putExtra("wid", wid);
in.putExtra("sob", gson.toJson(sob)); in.putExtra("sob", gson.toJson(sob));
in.putExtra("keys", key); in.putExtra("keys", key);
startActivity(in); startActivity(in);
break;
} else {
ToastUtils.showToast(StartYunDanActivity.this, "请先上传出货单,再进行卸货操作!");
}
} else {//大于五分钟可以接单
ToastUtils.showToast(StartYunDanActivity.this, "装卸货时间间隔过短,请稍后重试!");
}
break;
}
}
} }
} }
}); });
@@ -421,7 +440,8 @@ public class StartYunDanActivity extends BaseActivity {
//str="待卸货"; //str="待卸货";
str = "确认卸货"; str = "确认卸货";
break; break;
default:break; default:
break;
} }
return str; return str;
} }
@@ -457,6 +477,7 @@ public class StartYunDanActivity extends BaseActivity {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
map.onSaveInstanceState(outState); map.onSaveInstanceState(outState);
} }
LocationSource.OnLocationChangedListener mListener; LocationSource.OnLocationChangedListener mListener;
AMapLocationClient mlocationClient; AMapLocationClient mlocationClient;
AMapLocationClientOption mLocationOption; AMapLocationClientOption mLocationOption;
@@ -575,7 +596,6 @@ public class StartYunDanActivity extends BaseActivity {
} }
class OnRouteSearchListener implements RouteSearch.OnRouteSearchListener { class OnRouteSearchListener implements RouteSearch.OnRouteSearchListener {
@Override @Override
@@ -19,7 +19,6 @@ import com.alct.mdp.callback.OnResultListener;
import com.alct.mdp.model.Goods; import com.alct.mdp.model.Goods;
import com.alct.mdp.model.Image; import com.alct.mdp.model.Image;
import com.alct.mdp.model.Location; import com.alct.mdp.model.Location;
import com.arpa.hndahesudintocctmsdriver.H5Activity;
import com.arpa.hndahesudintocctmsdriver.bean.CarInfoBean; import com.arpa.hndahesudintocctmsdriver.bean.CarInfoBean;
import com.arpa.hndahesudintocctmsdriver.report.ALProcess; import com.arpa.hndahesudintocctmsdriver.report.ALProcess;
import com.arpa.hndahesudintocctmsdriver.report.JTTProcess; import com.arpa.hndahesudintocctmsdriver.report.JTTProcess;
@@ -30,7 +29,12 @@ import com.arpa.hndahesudintocctmsdriver.util.NfcUtils;
import com.arpa.hndahesudintocctmsdriver.util.alert.ToastUtil; import com.arpa.hndahesudintocctmsdriver.util.alert.ToastUtil;
import com.arpa.hndahesudintocctmsdriver.util.img.GetCarImageAlert; import com.arpa.hndahesudintocctmsdriver.util.img.GetCarImageAlert;
import com.arpa.hndahesudintocctmsdriver.util.location.LocationUtil; import com.arpa.hndahesudintocctmsdriver.util.location.LocationUtil;
import com.baidu.mapapi.CoordType;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.utils.CoordinateConverter;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.dahe.mylibrary.utils.ToastUtils;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureConfig;
@@ -233,14 +237,16 @@ public class ShangChuangImgActivity extends BaseAppCompatActivity {
lgdu.onCreate(); lgdu.onCreate();
lu = new LocationUtil(con, hd); lu = new LocationUtil(con, hd);
lu.onCreate(); lu.onCreate();
if (keys) { // if (keys) {
lu = new LocationUtil(con, hd); // lu = new LocationUtil(con, hd);
lu.onCreate(); // lu.onCreate();
} // }
tv_loaction.setOnClickListener(v -> { tv_loaction.setOnClickListener(v -> {
if (!locationKey) { if (!locationKey) {
customDialog.show(); customDialog.show();
lgdu.onCreate(); lgdu.onCreate();
lu = new LocationUtil(con, hd);
lu.onCreate();
} }
}); });
hyr = new HuoYuanRequset(con, hd); hyr = new HuoYuanRequset(con, hd);
@@ -348,9 +354,12 @@ public class ShangChuangImgActivity extends BaseAppCompatActivity {
lub.setImageId(imgIds); lub.setImageId(imgIds);
lub.setWaybillId(wid); lub.setWaybillId(wid);
Log.e("--装货信息", gson.toJson(lub)); Log.e("--装货信息", gson.toJson(lub));
if (locationKey) { if (locationKey) {//高德定位
if (keys) { if (keys) {
if (lu!=null&&lu.getErrorCode()!=161){
ToastUtils.showToast(this,"百度地图定位失败,请确认开启定位后重试!");
return;
}
nfc(); nfc();
loadInfo(); loadInfo();
// up_traffic_start(); // up_traffic_start();
@@ -375,6 +384,10 @@ public class ShangChuangImgActivity extends BaseAppCompatActivity {
Log.e("--卸货信息", gson.toJson(lub)); Log.e("--卸货信息", gson.toJson(lub));
if (locationKey) { if (locationKey) {
if (keys) { if (keys) {
if (lu!=null&&lu.getErrorCode()!=161){
ToastUtils.showToast(this,"百度地图定位失败,请确认开启定位后重试!");
return;
}
nfc(); nfc();
uploadInfo(); uploadInfo();
@@ -403,6 +416,10 @@ public class ShangChuangImgActivity extends BaseAppCompatActivity {
Log.e("--回单信息", gson.toJson(urib)); Log.e("--回单信息", gson.toJson(urib));
if (locationKey) { if (locationKey) {
if (index == 2 && keys) { if (index == 2 && keys) {
if (lu!=null&&lu.getErrorCode()!=161){
ToastUtils.showToast(this,"百度地图定位失败,请确认开启定位后重试!");
return;
}
up_trafficSign(); up_trafficSign();
up_trafficReceipt(); up_trafficReceipt();
receipt_image(); receipt_image();
@@ -20,6 +20,7 @@ public class LocationUtil{
private double latitude; private double latitude;
private double longitude; private double longitude;
private int errorCode;
private String address; private String address;
private Context con; private Context con;
private Handler hd; private Handler hd;
@@ -39,6 +40,10 @@ public class LocationUtil{
return longitude; return longitude;
} }
public double getErrorCode() {
return errorCode;
}
public String getAddress() { public String getAddress() {
return address; return address;
} }
@@ -62,7 +67,7 @@ public class LocationUtil{
//LocationMode.Hight_Accuracy:高精度; //LocationMode.Hight_Accuracy:高精度;
//LocationMode. Battery_Saving:低功耗; //LocationMode. Battery_Saving:低功耗;
//LocationMode. Device_Sensors:仅使用设备; //LocationMode. Device_Sensors:仅使用设备;
option.setCoorType("GCJ02"); option.setCoorType("BD09ll");
//可选,设置返回经纬度坐标类型,默认GCJ02 //可选,设置返回经纬度坐标类型,默认GCJ02
//GCJ02:国测局坐标; //GCJ02:国测局坐标;
//BD09ll:百度经纬度坐标; //BD09ll:百度经纬度坐标;
@@ -112,7 +117,8 @@ public class LocationUtil{
float radius = location.getRadius(); //获取定位精度,默认值为0.0f float radius = location.getRadius(); //获取定位精度,默认值为0.0f
String coorType = location.getCoorType(); String coorType = location.getCoorType();
//获取经纬度坐标类型,以LocationClientOption中设置过的坐标类型为准 //获取经纬度坐标类型,以LocationClientOption中设置过的坐标类型为准
int errorCode = location.getLocType(); errorCode = location.getLocType();
String locTypeDescription = location.getLocTypeDescription();
//获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明 //获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明
Log.e("定位位置address",address+""); Log.e("定位位置address",address+"");
Log.e("定位坐标latitude",latitude+""); Log.e("定位坐标latitude",latitude+"");
+424
View File
@@ -0,0 +1,424 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<dimen name="common_margin">@dimen/dp_15</dimen>
<dimen name="dp_m_60">-256.6667dp</dimen>
<dimen name="dp_m_50">-213.8889dp</dimen>
<dimen name="dp_m_30">-128.3333dp</dimen>
<dimen name="dp_m_20">-85.5556dp</dimen>
<dimen name="dp_m_12">-51.3333dp</dimen>
<dimen name="dp_m_10">-42.7778dp</dimen>
<dimen name="dp_m_8">-34.2222dp</dimen>
<dimen name="dp_m_5">-21.3889dp</dimen>
<dimen name="dp_m_2">-8.5556dp</dimen>
<dimen name="dp_m_1">-4.2778dp</dimen>
<dimen name="dp_0">0.0000dp</dimen>
<dimen name="dp_0_1">0.4278dp</dimen>
<dimen name="dp_0_5">2.1389dp</dimen>
<dimen name="dp_1">4.2778dp</dimen>
<dimen name="dp_1_5">6.4167dp</dimen>
<dimen name="dp_2">8.5556dp</dimen>
<dimen name="dp_2_5">10.6944dp</dimen>
<dimen name="dp_3">12.8333dp</dimen>
<dimen name="dp_3_5">14.9722dp</dimen>
<dimen name="dp_4">17.1111dp</dimen>
<dimen name="dp_4_5">19.2500dp</dimen>
<dimen name="dp_5">21.3889dp</dimen>
<dimen name="dp_6">25.6667dp</dimen>
<dimen name="dp_7">29.9444dp</dimen>
<dimen name="dp_8">34.2222dp</dimen>
<dimen name="dp_9">38.5000dp</dimen>
<dimen name="dp_10">42.7778dp</dimen>
<dimen name="dp_11">47.0556dp</dimen>
<dimen name="dp_12">51.3333dp</dimen>
<dimen name="dp_13">55.6111dp</dimen>
<dimen name="dp_14">59.8889dp</dimen>
<dimen name="dp_15">64.1667dp</dimen>
<dimen name="dp_16">68.4444dp</dimen>
<dimen name="dp_17">72.7222dp</dimen>
<dimen name="dp_18">77.0000dp</dimen>
<dimen name="dp_19">81.2778dp</dimen>
<dimen name="dp_20">85.5556dp</dimen>
<dimen name="dp_21">89.8333dp</dimen>
<dimen name="dp_22">94.1111dp</dimen>
<dimen name="dp_23">98.3889dp</dimen>
<dimen name="dp_24">102.6667dp</dimen>
<dimen name="dp_25">106.9444dp</dimen>
<dimen name="dp_26">111.2222dp</dimen>
<dimen name="dp_27">115.5000dp</dimen>
<dimen name="dp_28">119.7778dp</dimen>
<dimen name="dp_29">124.0556dp</dimen>
<dimen name="dp_30">128.3333dp</dimen>
<dimen name="dp_31">132.6111dp</dimen>
<dimen name="dp_32">136.8889dp</dimen>
<dimen name="dp_33">141.1667dp</dimen>
<dimen name="dp_34">145.4444dp</dimen>
<dimen name="dp_35">149.7222dp</dimen>
<dimen name="dp_36">154.0000dp</dimen>
<dimen name="dp_37">158.2778dp</dimen>
<dimen name="dp_38">162.5556dp</dimen>
<dimen name="dp_39">166.8333dp</dimen>
<dimen name="dp_40">171.1111dp</dimen>
<dimen name="dp_41">175.3889dp</dimen>
<dimen name="dp_42">179.6667dp</dimen>
<dimen name="dp_43">183.9444dp</dimen>
<dimen name="dp_44">188.2222dp</dimen>
<dimen name="dp_45">192.5000dp</dimen>
<dimen name="dp_46">196.7778dp</dimen>
<dimen name="dp_47">201.0556dp</dimen>
<dimen name="dp_48">205.3333dp</dimen>
<dimen name="dp_49">209.6111dp</dimen>
<dimen name="dp_50">213.8889dp</dimen>
<dimen name="dp_51">218.1667dp</dimen>
<dimen name="dp_52">222.4444dp</dimen>
<dimen name="dp_53">226.7222dp</dimen>
<dimen name="dp_54">231.0000dp</dimen>
<dimen name="dp_55">235.2778dp</dimen>
<dimen name="dp_56">239.5556dp</dimen>
<dimen name="dp_57">243.8333dp</dimen>
<dimen name="dp_58">248.1111dp</dimen>
<dimen name="dp_59">252.3889dp</dimen>
<dimen name="dp_60">256.6667dp</dimen>
<dimen name="dp_61">260.9444dp</dimen>
<dimen name="dp_62">265.2222dp</dimen>
<dimen name="dp_63">269.5000dp</dimen>
<dimen name="dp_64">273.7778dp</dimen>
<dimen name="dp_65">278.0556dp</dimen>
<dimen name="dp_66">282.3333dp</dimen>
<dimen name="dp_67">286.6111dp</dimen>
<dimen name="dp_68">290.8889dp</dimen>
<dimen name="dp_69">295.1667dp</dimen>
<dimen name="dp_70">299.4444dp</dimen>
<dimen name="dp_71">303.7222dp</dimen>
<dimen name="dp_72">308.0000dp</dimen>
<dimen name="dp_73">312.2778dp</dimen>
<dimen name="dp_74">316.5556dp</dimen>
<dimen name="dp_75">320.8333dp</dimen>
<dimen name="dp_76">325.1111dp</dimen>
<dimen name="dp_77">329.3889dp</dimen>
<dimen name="dp_78">333.6667dp</dimen>
<dimen name="dp_79">337.9444dp</dimen>
<dimen name="dp_80">342.2222dp</dimen>
<dimen name="dp_81">346.5000dp</dimen>
<dimen name="dp_82">350.7778dp</dimen>
<dimen name="dp_83">355.0556dp</dimen>
<dimen name="dp_84">359.3333dp</dimen>
<dimen name="dp_85">363.6111dp</dimen>
<dimen name="dp_86">367.8889dp</dimen>
<dimen name="dp_87">372.1667dp</dimen>
<dimen name="dp_88">376.4444dp</dimen>
<dimen name="dp_89">380.7222dp</dimen>
<dimen name="dp_90">385.0000dp</dimen>
<dimen name="dp_91">389.2778dp</dimen>
<dimen name="dp_92">393.5556dp</dimen>
<dimen name="dp_93">397.8333dp</dimen>
<dimen name="dp_94">402.1111dp</dimen>
<dimen name="dp_95">406.3889dp</dimen>
<dimen name="dp_96">410.6667dp</dimen>
<dimen name="dp_97">414.9444dp</dimen>
<dimen name="dp_98">419.2222dp</dimen>
<dimen name="dp_99">423.5000dp</dimen>
<dimen name="dp_100">427.7778dp</dimen>
<dimen name="dp_101">432.0556dp</dimen>
<dimen name="dp_102">436.3333dp</dimen>
<dimen name="dp_103">440.6111dp</dimen>
<dimen name="dp_104">444.8889dp</dimen>
<dimen name="dp_105">449.1667dp</dimen>
<dimen name="dp_106">453.4444dp</dimen>
<dimen name="dp_107">457.7222dp</dimen>
<dimen name="dp_108">462.0000dp</dimen>
<dimen name="dp_109">466.2778dp</dimen>
<dimen name="dp_110">470.5556dp</dimen>
<dimen name="dp_111">474.8333dp</dimen>
<dimen name="dp_112">479.1111dp</dimen>
<dimen name="dp_113">483.3889dp</dimen>
<dimen name="dp_114">487.6667dp</dimen>
<dimen name="dp_115">491.9444dp</dimen>
<dimen name="dp_116">496.2222dp</dimen>
<dimen name="dp_117">500.5000dp</dimen>
<dimen name="dp_118">504.7778dp</dimen>
<dimen name="dp_119">509.0556dp</dimen>
<dimen name="dp_120">513.3333dp</dimen>
<dimen name="dp_121">517.6111dp</dimen>
<dimen name="dp_122">521.8889dp</dimen>
<dimen name="dp_123">526.1667dp</dimen>
<dimen name="dp_124">530.4444dp</dimen>
<dimen name="dp_125">534.7222dp</dimen>
<dimen name="dp_126">539.0000dp</dimen>
<dimen name="dp_127">543.2778dp</dimen>
<dimen name="dp_128">547.5556dp</dimen>
<dimen name="dp_129">551.8333dp</dimen>
<dimen name="dp_130">556.1111dp</dimen>
<dimen name="dp_131">560.3889dp</dimen>
<dimen name="dp_132">564.6667dp</dimen>
<dimen name="dp_133">568.9444dp</dimen>
<dimen name="dp_134">573.2222dp</dimen>
<dimen name="dp_135">577.5000dp</dimen>
<dimen name="dp_136">581.7778dp</dimen>
<dimen name="dp_137">586.0556dp</dimen>
<dimen name="dp_138">590.3333dp</dimen>
<dimen name="dp_139">594.6111dp</dimen>
<dimen name="dp_140">598.8889dp</dimen>
<dimen name="dp_141">603.1667dp</dimen>
<dimen name="dp_142">607.4444dp</dimen>
<dimen name="dp_143">611.7222dp</dimen>
<dimen name="dp_144">616.0000dp</dimen>
<dimen name="dp_145">620.2778dp</dimen>
<dimen name="dp_146">624.5556dp</dimen>
<dimen name="dp_147">628.8333dp</dimen>
<dimen name="dp_148">633.1111dp</dimen>
<dimen name="dp_149">637.3889dp</dimen>
<dimen name="dp_150">641.6667dp</dimen>
<dimen name="dp_151">645.9444dp</dimen>
<dimen name="dp_152">650.2222dp</dimen>
<dimen name="dp_153">654.5000dp</dimen>
<dimen name="dp_154">658.7778dp</dimen>
<dimen name="dp_155">663.0556dp</dimen>
<dimen name="dp_156">667.3333dp</dimen>
<dimen name="dp_157">671.6111dp</dimen>
<dimen name="dp_158">675.8889dp</dimen>
<dimen name="dp_159">680.1667dp</dimen>
<dimen name="dp_160">684.4444dp</dimen>
<dimen name="dp_161">688.7222dp</dimen>
<dimen name="dp_162">693.0000dp</dimen>
<dimen name="dp_163">697.2778dp</dimen>
<dimen name="dp_164">701.5556dp</dimen>
<dimen name="dp_165">705.8333dp</dimen>
<dimen name="dp_166">710.1111dp</dimen>
<dimen name="dp_167">714.3889dp</dimen>
<dimen name="dp_168">718.6667dp</dimen>
<dimen name="dp_169">722.9444dp</dimen>
<dimen name="dp_170">727.2222dp</dimen>
<dimen name="dp_171">731.5000dp</dimen>
<dimen name="dp_172">735.7778dp</dimen>
<dimen name="dp_173">740.0556dp</dimen>
<dimen name="dp_174">744.3333dp</dimen>
<dimen name="dp_175">748.6111dp</dimen>
<dimen name="dp_176">752.8889dp</dimen>
<dimen name="dp_177">757.1667dp</dimen>
<dimen name="dp_178">761.4444dp</dimen>
<dimen name="dp_179">765.7222dp</dimen>
<dimen name="dp_180">770.0000dp</dimen>
<dimen name="dp_181">774.2778dp</dimen>
<dimen name="dp_182">778.5556dp</dimen>
<dimen name="dp_183">782.8333dp</dimen>
<dimen name="dp_184">787.1111dp</dimen>
<dimen name="dp_185">791.3889dp</dimen>
<dimen name="dp_186">795.6667dp</dimen>
<dimen name="dp_187">799.9444dp</dimen>
<dimen name="dp_188">804.2222dp</dimen>
<dimen name="dp_189">808.5000dp</dimen>
<dimen name="dp_190">812.7778dp</dimen>
<dimen name="dp_191">817.0556dp</dimen>
<dimen name="dp_192">821.3333dp</dimen>
<dimen name="dp_193">825.6111dp</dimen>
<dimen name="dp_194">829.8889dp</dimen>
<dimen name="dp_195">834.1667dp</dimen>
<dimen name="dp_196">838.4444dp</dimen>
<dimen name="dp_197">842.7222dp</dimen>
<dimen name="dp_198">847.0000dp</dimen>
<dimen name="dp_199">851.2778dp</dimen>
<dimen name="dp_200">855.5556dp</dimen>
<dimen name="dp_201">859.8333dp</dimen>
<dimen name="dp_202">864.1111dp</dimen>
<dimen name="dp_203">868.3889dp</dimen>
<dimen name="dp_204">872.6667dp</dimen>
<dimen name="dp_205">876.9444dp</dimen>
<dimen name="dp_206">881.2222dp</dimen>
<dimen name="dp_207">885.5000dp</dimen>
<dimen name="dp_208">889.7778dp</dimen>
<dimen name="dp_209">894.0556dp</dimen>
<dimen name="dp_210">898.3333dp</dimen>
<dimen name="dp_211">902.6111dp</dimen>
<dimen name="dp_212">906.8889dp</dimen>
<dimen name="dp_213">911.1667dp</dimen>
<dimen name="dp_214">915.4444dp</dimen>
<dimen name="dp_215">919.7222dp</dimen>
<dimen name="dp_216">924.0000dp</dimen>
<dimen name="dp_217">928.2778dp</dimen>
<dimen name="dp_218">932.5556dp</dimen>
<dimen name="dp_219">936.8333dp</dimen>
<dimen name="dp_220">941.1111dp</dimen>
<dimen name="dp_221">945.3889dp</dimen>
<dimen name="dp_222">949.6667dp</dimen>
<dimen name="dp_223">953.9444dp</dimen>
<dimen name="dp_224">958.2222dp</dimen>
<dimen name="dp_225">962.5000dp</dimen>
<dimen name="dp_226">966.7778dp</dimen>
<dimen name="dp_227">971.0556dp</dimen>
<dimen name="dp_228">975.3333dp</dimen>
<dimen name="dp_229">979.6111dp</dimen>
<dimen name="dp_230">983.8889dp</dimen>
<dimen name="dp_231">988.1667dp</dimen>
<dimen name="dp_232">992.4444dp</dimen>
<dimen name="dp_233">996.7222dp</dimen>
<dimen name="dp_234">1001.0000dp</dimen>
<dimen name="dp_235">1005.2778dp</dimen>
<dimen name="dp_236">1009.5556dp</dimen>
<dimen name="dp_237">1013.8333dp</dimen>
<dimen name="dp_238">1018.1111dp</dimen>
<dimen name="dp_239">1022.3889dp</dimen>
<dimen name="dp_240">1026.6667dp</dimen>
<dimen name="dp_241">1030.9444dp</dimen>
<dimen name="dp_242">1035.2222dp</dimen>
<dimen name="dp_243">1039.5000dp</dimen>
<dimen name="dp_244">1043.7778dp</dimen>
<dimen name="dp_245">1048.0556dp</dimen>
<dimen name="dp_246">1052.3333dp</dimen>
<dimen name="dp_247">1056.6111dp</dimen>
<dimen name="dp_248">1060.8889dp</dimen>
<dimen name="dp_249">1065.1667dp</dimen>
<dimen name="dp_250">1069.4444dp</dimen>
<dimen name="dp_251">1073.7222dp</dimen>
<dimen name="dp_252">1078.0000dp</dimen>
<dimen name="dp_253">1082.2778dp</dimen>
<dimen name="dp_254">1086.5556dp</dimen>
<dimen name="dp_255">1090.8333dp</dimen>
<dimen name="dp_256">1095.1111dp</dimen>
<dimen name="dp_257">1099.3889dp</dimen>
<dimen name="dp_258">1103.6667dp</dimen>
<dimen name="dp_259">1107.9444dp</dimen>
<dimen name="dp_260">1112.2222dp</dimen>
<dimen name="dp_261">1116.5000dp</dimen>
<dimen name="dp_262">1120.7778dp</dimen>
<dimen name="dp_263">1125.0556dp</dimen>
<dimen name="dp_264">1129.3333dp</dimen>
<dimen name="dp_265">1133.6111dp</dimen>
<dimen name="dp_266">1137.8889dp</dimen>
<dimen name="dp_267">1142.1667dp</dimen>
<dimen name="dp_268">1146.4444dp</dimen>
<dimen name="dp_269">1150.7222dp</dimen>
<dimen name="dp_270">1155.0000dp</dimen>
<dimen name="dp_271">1159.2778dp</dimen>
<dimen name="dp_272">1163.5556dp</dimen>
<dimen name="dp_273">1167.8333dp</dimen>
<dimen name="dp_274">1172.1111dp</dimen>
<dimen name="dp_275">1176.3889dp</dimen>
<dimen name="dp_276">1180.6667dp</dimen>
<dimen name="dp_277">1184.9444dp</dimen>
<dimen name="dp_278">1189.2222dp</dimen>
<dimen name="dp_279">1193.5000dp</dimen>
<dimen name="dp_280">1197.7778dp</dimen>
<dimen name="dp_281">1202.0556dp</dimen>
<dimen name="dp_282">1206.3333dp</dimen>
<dimen name="dp_283">1210.6111dp</dimen>
<dimen name="dp_284">1214.8889dp</dimen>
<dimen name="dp_285">1219.1667dp</dimen>
<dimen name="dp_286">1223.4444dp</dimen>
<dimen name="dp_287">1227.7222dp</dimen>
<dimen name="dp_288">1232.0000dp</dimen>
<dimen name="dp_289">1236.2778dp</dimen>
<dimen name="dp_290">1240.5556dp</dimen>
<dimen name="dp_291">1244.8333dp</dimen>
<dimen name="dp_292">1249.1111dp</dimen>
<dimen name="dp_293">1253.3889dp</dimen>
<dimen name="dp_294">1257.6667dp</dimen>
<dimen name="dp_295">1261.9444dp</dimen>
<dimen name="dp_296">1266.2222dp</dimen>
<dimen name="dp_297">1270.5000dp</dimen>
<dimen name="dp_298">1274.7778dp</dimen>
<dimen name="dp_299">1279.0556dp</dimen>
<dimen name="dp_300">1283.3333dp</dimen>
<dimen name="dp_301">1287.6111dp</dimen>
<dimen name="dp_302">1291.8889dp</dimen>
<dimen name="dp_303">1296.1667dp</dimen>
<dimen name="dp_304">1300.4444dp</dimen>
<dimen name="dp_305">1304.7222dp</dimen>
<dimen name="dp_306">1309.0000dp</dimen>
<dimen name="dp_307">1313.2778dp</dimen>
<dimen name="dp_308">1317.5556dp</dimen>
<dimen name="dp_309">1321.8333dp</dimen>
<dimen name="dp_310">1326.1111dp</dimen>
<dimen name="dp_311">1330.3889dp</dimen>
<dimen name="dp_312">1334.6667dp</dimen>
<dimen name="dp_313">1338.9444dp</dimen>
<dimen name="dp_314">1343.2222dp</dimen>
<dimen name="dp_315">1347.5000dp</dimen>
<dimen name="dp_316">1351.7778dp</dimen>
<dimen name="dp_317">1356.0556dp</dimen>
<dimen name="dp_318">1360.3333dp</dimen>
<dimen name="dp_319">1364.6111dp</dimen>
<dimen name="dp_320">1368.8889dp</dimen>
<dimen name="dp_321">1373.1667dp</dimen>
<dimen name="dp_322">1377.4444dp</dimen>
<dimen name="dp_323">1381.7222dp</dimen>
<dimen name="dp_324">1386.0000dp</dimen>
<dimen name="dp_325">1390.2778dp</dimen>
<dimen name="dp_326">1394.5556dp</dimen>
<dimen name="dp_327">1398.8333dp</dimen>
<dimen name="dp_328">1403.1111dp</dimen>
<dimen name="dp_329">1407.3889dp</dimen>
<dimen name="dp_330">1411.6667dp</dimen>
<dimen name="dp_331">1415.9444dp</dimen>
<dimen name="dp_332">1420.2222dp</dimen>
<dimen name="dp_333">1424.5000dp</dimen>
<dimen name="dp_334">1428.7778dp</dimen>
<dimen name="dp_335">1433.0556dp</dimen>
<dimen name="dp_336">1437.3333dp</dimen>
<dimen name="dp_337">1441.6111dp</dimen>
<dimen name="dp_338">1445.8889dp</dimen>
<dimen name="dp_339">1450.1667dp</dimen>
<dimen name="dp_340">1454.4444dp</dimen>
<dimen name="dp_341">1458.7222dp</dimen>
<dimen name="dp_342">1463.0000dp</dimen>
<dimen name="dp_343">1467.2778dp</dimen>
<dimen name="dp_344">1471.5556dp</dimen>
<dimen name="dp_345">1475.8333dp</dimen>
<dimen name="dp_346">1480.1111dp</dimen>
<dimen name="dp_347">1484.3889dp</dimen>
<dimen name="dp_348">1488.6667dp</dimen>
<dimen name="dp_349">1492.9444dp</dimen>
<dimen name="dp_350">1497.2222dp</dimen>
<dimen name="dp_351">1501.5000dp</dimen>
<dimen name="dp_352">1505.7778dp</dimen>
<dimen name="dp_353">1510.0556dp</dimen>
<dimen name="dp_354">1514.3333dp</dimen>
<dimen name="dp_355">1518.6111dp</dimen>
<dimen name="dp_356">1522.8889dp</dimen>
<dimen name="dp_357">1527.1667dp</dimen>
<dimen name="dp_358">1531.4444dp</dimen>
<dimen name="dp_359">1535.7222dp</dimen>
<dimen name="dp_360">1540.0000dp</dimen>
<dimen name="dp_365">1561.3889dp</dimen>
<dimen name="dp_370">1582.7778dp</dimen>
<dimen name="dp_380">1625.5556dp</dimen>
<dimen name="dp_385">1646.9444dp</dimen>
<dimen name="dp_400">1711.1111dp</dimen>
<dimen name="dp_410">1753.8889dp</dimen>
<dimen name="dp_422">1805.2222dp</dimen>
<dimen name="dp_472">2019.1111dp</dimen>
<dimen name="dp_500">2138.8889dp</dimen>
<dimen name="dp_600">2566.6667dp</dimen>
<dimen name="dp_640">2737.7778dp</dimen>
<dimen name="dp_720">3080.0000dp</dimen>
<dimen name="sp_6">25.6667dp</dimen>
<dimen name="sp_7">29.9444dp</dimen>
<dimen name="sp_8">34.2222dp</dimen>
<dimen name="sp_9">38.5000dp</dimen>
<dimen name="sp_10">42.7778dp</dimen>
<dimen name="sp_11">47.0556dp</dimen>
<dimen name="sp_12">51.3333dp</dimen>
<dimen name="sp_13">55.6111dp</dimen>
<dimen name="sp_14">59.8889dp</dimen>
<dimen name="sp_15">64.1667dp</dimen>
<dimen name="sp_16">68.4444dp</dimen>
<dimen name="sp_17">72.7222dp</dimen>
<dimen name="sp_18">77.0000dp</dimen>
<dimen name="sp_19">81.2778dp</dimen>
<dimen name="sp_20">85.5556dp</dimen>
<dimen name="sp_21">89.8333dp</dimen>
<dimen name="sp_22">94.1111dp</dimen>
<dimen name="sp_23">98.3889dp</dimen>
<dimen name="sp_24">102.6667dp</dimen>
<dimen name="sp_25">106.9444dp</dimen>
<dimen name="sp_28">119.7778dp</dimen>
<dimen name="sp_30">128.3333dp</dimen>
<dimen name="sp_32">136.8889dp</dimen>
<dimen name="sp_34">145.4444dp</dimen>
<dimen name="sp_36">154.0000dp</dimen>
<dimen name="sp_38">162.5556dp</dimen>
<dimen name="sp_40">171.1111dp</dimen>
<dimen name="sp_42">179.6667dp</dimen>
<dimen name="sp_48">205.3333dp</dimen>
<dimen name="sp_58">248.1111dp</dimen>
<dimen name="sp_115">491.9444dp</dimen>
</resources>
+1 -1
View File
@@ -1,5 +1,5 @@
<resources> <resources>
<string name="app_name">大河好运司机</string> <!-- <string name="appName">大河好运司机</string>-->
<!--设置界面--> <!--设置界面-->
<string name="alivc_recorder_setting_min_record_duration">最小时长</string> <string name="alivc_recorder_setting_min_record_duration">最小时长</string>
<string name="alivc_recorder_setting_max_record_duration">最大时长</string> <string name="alivc_recorder_setting_max_record_duration">最大时长</string>
@@ -179,6 +179,7 @@ public final class TimeUtil {
private static final DateFormat DEFAULT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); private static final DateFormat DEFAULT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
private static final DateFormat DEFAULT_FORMAT2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZSS:SS", Locale.getDefault()); private static final DateFormat DEFAULT_FORMAT2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZSS:SS", Locale.getDefault());
private static final DateFormat DEFAULT_FORMAT3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault()); private static final DateFormat DEFAULT_FORMAT3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());
public static final DateFormat DEFAULT_FORMAT4 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
private TimeUtil() { private TimeUtil() {
throw new UnsupportedOperationException("u can't instantiate me..."); throw new UnsupportedOperationException("u can't instantiate me...");
@@ -1700,4 +1701,22 @@ public final class TimeUtil {
return false; return false;
} }
} }
/**
* 得到二个日期间的间隔天数
*
* @return 间格的天数(null表示时间格式不正确)
*/
public static int compareNowDate(long comTime) {
int min;
try {
min = (int) ((getNowMills() - comTime) / (60 * 1000));
} catch (Exception e) {
e.printStackTrace();
return 0;
}
return min;
}
} }
+1
View File
@@ -0,0 +1 @@
/build
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:27.1.1'
testCompile 'junit:junit:4.12'
compile files('libs/license.jar')
}
Binary file not shown.
+18
View File
@@ -0,0 +1,18 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/baidu/IDE/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-dontwarn com.baidu.ocr.**
+32
View File
@@ -0,0 +1,32 @@
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.baidu.ocr.ui">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera2.full" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:vmSafeMode="true"
tools:replace="android:allowBackup,android:icon">
<activity
android:name="com.baidu.ocr.ui.camera.CameraActivity"
android:configChanges="screenSize|orientation"
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" />
</application>
</manifest>
@@ -0,0 +1,128 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idcardquality;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import com.baidu.idl.authority.AlgorithmOnMainThreadException;
import com.baidu.idl.authority.IDLAuthorityException;
import com.baidu.idl.license.License;
import com.baidu.idl.util.UIThread;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class IDcardQualityProcess {
final ReentrantReadWriteLock nativeModelLock = new ReentrantReadWriteLock();
private static IDcardQualityProcess mInstance;
private static String tokenString;
private static int mAuthorityStatus;
private static Throwable loadNativeException = null;
private static volatile boolean hasReleased;
public IDcardQualityProcess() {
}
public static synchronized IDcardQualityProcess getInstance() {
if (null == mInstance) {
mInstance = new IDcardQualityProcess();
}
return mInstance;
}
public native byte[] convertRGBImage(int[] colors, int width, int height);
public native int idcardQualityModelInit(AssetManager var1, String var2);
public native int idcardQualityCaptchaRelease();
public native int idcardQualityProcess(byte[] var1, int var2, int var3, boolean var4, int var5);
public static synchronized int init(String token) throws AlgorithmOnMainThreadException, IDLAuthorityException {
if (UIThread.isUITread()) {
throw new AlgorithmOnMainThreadException();
} else {
tokenString = token;
try {
mAuthorityStatus = License.getInstance().init(tokenString);
} catch (Exception var2) {
var2.printStackTrace();
}
return mAuthorityStatus;
}
}
public int idcardQualityInit(AssetManager assetManager, String modelPath) {
if (mAuthorityStatus == 0) {
hasReleased = false;
nativeModelLock.writeLock().lock();
int status = this.idcardQualityModelInit(assetManager, modelPath);
nativeModelLock.writeLock().unlock();
return status;
} else {
return mAuthorityStatus;
}
}
public int idcardQualityRelease() {
if (mAuthorityStatus == 0) {
hasReleased = true;
nativeModelLock.writeLock().lock();
this.idcardQualityCaptchaRelease();
nativeModelLock.writeLock().unlock();
return 0;
} else {
return mAuthorityStatus;
}
}
public int idcardQualityDetectionImg(Bitmap img, boolean isfont) {
int status;
if (mAuthorityStatus == 0) {
if (hasReleased) {
return -1;
}
int imgHeight = img.getHeight();
int imgWidth = img.getWidth();
byte[] imageData = this.getRGBImageData(img);
nativeModelLock.readLock().lock();
status = this.idcardQualityProcess(imageData, imgHeight, imgWidth, isfont, 3);
nativeModelLock.readLock().unlock();
return status;
} else {
return mAuthorityStatus;
}
}
public static Throwable getLoadSoException() {
return loadNativeException;
}
public byte[] getRGBImageData(Bitmap img) {
int imgWidth = img.getWidth();
int imgHeight = img.getHeight();
int[] pixels = new int[imgWidth * imgHeight];
img.getPixels(pixels, 0, imgWidth, 0, 0, imgWidth, imgHeight);
byte[] imageData = convertRGBImage(pixels, imgWidth, imgHeight);
return imageData;
}
public void releaseModel() {
this.idcardQualityRelease();
}
static {
try {
System.loadLibrary("idl_license");
System.loadLibrary("idcard_quality.1.1.1");
} catch (Throwable e) {
loadNativeException = e;
}
mInstance = null;
mAuthorityStatus = 256;
}
}
@@ -0,0 +1,573 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import androidx.core.app.ActivityCompat;
/**
* 5.0以下相机API的封装。
*/
@SuppressWarnings("deprecation")
public class Camera1Control implements ICameraControl {
private int displayOrientation = 0;
private int cameraId = 0;
private int flashMode;
private AtomicBoolean takingPicture = new AtomicBoolean(false);
private AtomicBoolean abortingScan = new AtomicBoolean(false);
private Context context;
private Camera camera;
private Camera.Parameters parameters;
private PermissionCallback permissionCallback;
private Rect previewFrame = new Rect();
private PreviewView previewView;
private View displayView;
private int rotation = 0;
private OnDetectPictureCallback detectCallback;
private int previewFrameCount = 0;
private Camera.Size optSize;
/*
* 非扫描模式
*/
private final int MODEL_NOSCAN = 0;
/*
* 本地质量控制扫描模式
*/
private final int MODEL_SCAN = 1;
private int detectType = MODEL_NOSCAN;
public int getCameraRotation() {
return rotation;
}
public AtomicBoolean getAbortingScan() {
return abortingScan;
}
@Override
public void setDetectCallback(OnDetectPictureCallback callback) {
detectType = MODEL_SCAN;
detectCallback = callback;
}
private void onRequestDetect(byte[] data) {
// 相机已经关闭
if (camera == null || data == null || optSize == null) {
return;
}
YuvImage img = new YuvImage(data, ImageFormat.NV21, optSize.width, optSize.height, null);
ByteArrayOutputStream os = null;
try {
os = new ByteArrayOutputStream(data.length);
img.compressToJpeg(new Rect(0, 0, optSize.width, optSize.height), 80, os);
byte[] jpeg = os.toByteArray();
int status = detectCallback.onDetect(jpeg, getCameraRotation());
if (status == 0) {
clearPreviewCallback();
}
} catch (OutOfMemoryError e) {
// 内存溢出则取消当次操作
} finally {
try {
os.close();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setDisplayOrientation(@CameraView.Orientation int displayOrientation) {
this.displayOrientation = displayOrientation;
switch (displayOrientation) {
case CameraView.ORIENTATION_PORTRAIT:
rotation = 90;
break;
case CameraView.ORIENTATION_HORIZONTAL:
rotation = 0;
break;
case CameraView.ORIENTATION_INVERT:
rotation = 180;
break;
default:
rotation = 0;
}
previewView.requestLayout();
}
/**
* {@inheritDoc}
*/
@Override
public void refreshPermission() {
startPreview(true);
}
/**
* {@inheritDoc}
*/
@Override
public void setFlashMode(@FlashMode int flashMode) {
if (this.flashMode == flashMode) {
return;
}
this.flashMode = flashMode;
updateFlashMode(flashMode);
}
@Override
public int getFlashMode() {
return flashMode;
}
@Override
public void start() {
startPreview(false);
}
@Override
public void stop() {
if (camera != null) {
camera.setPreviewCallback(null);
stopPreview();
// 避免同步代码,为了先设置null后release
Camera tempC = camera;
camera = null;
tempC.release();
camera = null;
buffer = null;
}
}
private void stopPreview() {
if (camera != null) {
camera.stopPreview();
}
}
@Override
public void pause() {
if (camera != null) {
stopPreview();
}
setFlashMode(FLASH_MODE_OFF);
}
@Override
public void resume() {
takingPicture.set(false);
if (camera == null) {
openCamera();
} else {
previewView.textureView.setSurfaceTextureListener(surfaceTextureListener);
if (previewView.textureView.isAvailable()) {
startPreview(false);
}
}
}
@Override
public View getDisplayView() {
return displayView;
}
@Override
public void takePicture(final OnTakePictureCallback onTakePictureCallback) {
if (takingPicture.get()) {
return;
}
switch (displayOrientation) {
case CameraView.ORIENTATION_PORTRAIT:
parameters.setRotation(90);
break;
case CameraView.ORIENTATION_HORIZONTAL:
parameters.setRotation(0);
break;
case CameraView.ORIENTATION_INVERT:
parameters.setRotation(180);
break;
}
try {
Camera.Size picSize = getOptimalSize(camera.getParameters().getSupportedPictureSizes());
parameters.setPictureSize(picSize.width, picSize.height);
camera.setParameters(parameters);
takingPicture.set(true);
cancelAutoFocus();
CameraThreadPool.execute(new Runnable() {
@Override
public void run() {
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
startPreview(false);
takingPicture.set(false);
if (onTakePictureCallback != null) {
onTakePictureCallback.onPictureTaken(data);
}
}
});
}
});
} catch (RuntimeException e) {
e.printStackTrace();
startPreview(false);;
takingPicture.set(false);
}
}
@Override
public void setPermissionCallback(PermissionCallback callback) {
this.permissionCallback = callback;
}
public Camera1Control(Context context) {
this.context = context;
previewView = new PreviewView(context);
openCamera();
}
private void openCamera() {
setupDisplayView();
}
private void setupDisplayView() {
final TextureView textureView = new TextureView(context);
previewView.textureView = textureView;
previewView.setTextureView(textureView);
displayView = previewView;
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
private SurfaceTexture surfaceCache;
private byte[] buffer = null;
private void setPreviewCallbackImpl() {
if (buffer == null) {
buffer = new byte[displayView.getWidth()
* displayView.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8];
}
if (camera != null && detectType == MODEL_SCAN) {
camera.addCallbackBuffer(buffer);
camera.setPreviewCallback(previewCallback);
}
}
private void clearPreviewCallback() {
if (camera != null && detectType == MODEL_SCAN) {
camera.setPreviewCallback(null);
stopPreview();
}
}
Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(final byte[] data, Camera camera) {
// 扫描成功阻止打开新线程处理
if (abortingScan.get()) {
return;
}
// 节流
if (previewFrameCount++ % 5 != 0) {
return;
}
// 在某些机型和某项项目中,某些帧的data的数据不符合nv21的格式,需要过滤,否则后续处理会导致crash
if (data.length != parameters.getPreviewSize().width * parameters.getPreviewSize().height * 1.5) {
return;
}
camera.addCallbackBuffer(buffer);
CameraThreadPool.execute(new Runnable() {
@Override
public void run() {
Camera1Control.this.onRequestDetect(data);
}
});
}
};
private void initCamera() {
try {
if (camera == null) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
cameraId = i;
}
}
try {
camera = Camera.open(cameraId);
} catch (Throwable e) {
e.printStackTrace();
startPreview(true);
return;
}
}
if (parameters == null) {
parameters = camera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
}
opPreviewSize(previewView.getWidth(), previewView.getHeight());
camera.setPreviewTexture(surfaceCache);
setPreviewCallbackImpl();
startPreview(false);
} catch (IOException e) {
e.printStackTrace();
}
}
private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
surfaceCache = surface;
initCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
opPreviewSize(previewView.getWidth(), previewView.getHeight());
startPreview(false);
setPreviewCallbackImpl();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
setPreviewCallbackImpl();
}
};
// 开启预览
private void startPreview(boolean checkPermission) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
if (checkPermission && permissionCallback != null) {
permissionCallback.onRequestPermission();
}
return;
}
if (camera == null) {
initCamera();
} else {
camera.startPreview();
startAutoFocus();
}
}
private void cancelAutoFocus() {
camera.cancelAutoFocus();
CameraThreadPool.cancelAutoFocusTimer();
}
private void startAutoFocus() {
CameraThreadPool.createAutoFocusTimerTask(new Runnable() {
@Override
public void run() {
synchronized (Camera1Control.this) {
if (camera != null && !takingPicture.get()) {
try {
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
}
});
} catch (Throwable e) {
// startPreview是异步实现,可能在某些机器上前几次调用会autofocus failß
}
}
}
}
});
}
private void opPreviewSize(int width, @SuppressWarnings("unused") int height) {
if (parameters != null && camera != null && width > 0) {
optSize = getOptimalSize(camera.getParameters().getSupportedPreviewSizes());
parameters.setPreviewSize(optSize.width, optSize.height);
previewView.setRatio(1.0f * optSize.width / optSize.height);
camera.setDisplayOrientation(getSurfaceOrientation());
stopPreview();
try {
camera.setParameters(parameters);
} catch (RuntimeException e) {
e.printStackTrace();
}
}
}
private Camera.Size getOptimalSize(List<Camera.Size> sizes) {
int width = previewView.textureView.getWidth();
int height = previewView.textureView.getHeight();
Camera.Size pictureSize = sizes.get(0);
List<Camera.Size> candidates = new ArrayList<>();
for (Camera.Size size : sizes) {
if (size.width >= width && size.height >= height && size.width * height == size.height * width) {
// 比例相同
candidates.add(size);
} else if (size.height >= width && size.width >= height && size.width * width == size.height * height) {
// 反比例
candidates.add(size);
}
}
if (!candidates.isEmpty()) {
return Collections.min(candidates, sizeComparator);
}
for (Camera.Size size : sizes) {
if (size.width > width && size.height > height) {
return size;
}
}
return pictureSize;
}
private Comparator<Camera.Size> sizeComparator = new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
return Long.signum((long) lhs.width * lhs.height - (long) rhs.width * rhs.height);
}
};
private void updateFlashMode(int flashMode) {
switch (flashMode) {
case FLASH_MODE_TORCH:
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
break;
case FLASH_MODE_OFF:
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
break;
case ICameraControl.FLASH_MODE_AUTO:
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
break;
default:
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
break;
}
camera.setParameters(parameters);
}
private int getSurfaceOrientation() {
@CameraView.Orientation
int orientation = displayOrientation;
switch (orientation) {
case CameraView.ORIENTATION_PORTRAIT:
return 90;
case CameraView.ORIENTATION_HORIZONTAL:
return 0;
case CameraView.ORIENTATION_INVERT:
return 180;
default:
return 90;
}
}
/**
* 有些相机匹配不到完美的比例。比如。我们的layout是4:3的。预览只有16:9
* 的,如果直接显示图片会拉伸,变形。缩放的话,又有黑边。所以我们采取的策略
* 是,等比例放大。这样预览的有一部分会超出屏幕。拍照后再进行裁剪处理。
*/
private class PreviewView extends FrameLayout {
private TextureView textureView;
private float ratio = 0.75f;
void setTextureView(TextureView textureView) {
this.textureView = textureView;
removeAllViews();
addView(textureView);
}
void setRatio(float ratio) {
this.ratio = ratio;
requestLayout();
relayout(getWidth(), getHeight());
}
public PreviewView(Context context) {
super(context);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
relayout(w, h);
}
private void relayout(int w, int h) {
int width = w;
int height = h;
if (w < h) {
// 垂直模式,高度固定。
height = (int) (width * ratio);
} else {
// 水平模式,宽度固定。
width = (int) (height * ratio);
}
int l = (getWidth() - width) / 2;
int t = (getHeight() - height) / 2;
previewFrame.left = l;
previewFrame.top = t;
previewFrame.right = l + width;
previewFrame.bottom = t + height;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
textureView.layout(previewFrame.left, previewFrame.top, previewFrame.right, previewFrame.bottom);
}
}
@Override
public Rect getPreviewFrame() {
return previewFrame;
}
}
@@ -0,0 +1,697 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class Camera2Control implements ICameraControl {
@Override
public void setDetectCallback(OnDetectPictureCallback callback) {
// TODO 暂时只用camera
}
@Override
public AtomicBoolean getAbortingScan() {
// TODO 暂时只用camera
return null;
}
/**
* Conversion from screen rotation to JPEG orientation.
*/
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private static final int MAX_PREVIEW_SIZE = 2048;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private static final int STATE_PREVIEW = 0;
private static final int STATE_WAITING_FOR_LOCK = 1;
private static final int STATE_WAITING_FOR_CAPTURE = 2;
private static final int STATE_CAPTURING = 3;
private static final int STATE_PICTURE_TAKEN = 4;
private static final int MAX_PREVIEW_WIDTH = 1920;
private static final int MAX_PREVIEW_HEIGHT = 1080;
private int flashMode;
private int orientation = 0;
private int state = STATE_PREVIEW;
private Context context;
private OnTakePictureCallback onTakePictureCallback;
private PermissionCallback permissionCallback;
private String cameraId;
private TextureView textureView;
private Size previewSize;
private Rect previewFrame = new Rect();
private HandlerThread backgroundThread;
private Handler backgroundHandler;
private ImageReader imageReader;
private CameraCaptureSession captureSession;
private CameraDevice cameraDevice;
private CaptureRequest.Builder previewRequestBuilder;
private CaptureRequest previewRequest;
private Semaphore cameraLock = new Semaphore(1);
private int sensorOrientation;
@Override
public void start() {
startBackgroundThread();
if (textureView.isAvailable()) {
openCamera(textureView.getWidth(), textureView.getHeight());
textureView.setSurfaceTextureListener(surfaceTextureListener);
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
public void stop() {
textureView.setSurfaceTextureListener(null);
closeCamera();
stopBackgroundThread();
}
@Override
public void pause() {
setFlashMode(FLASH_MODE_OFF);
}
@Override
public void resume() {
state = STATE_PREVIEW;
}
@Override
public View getDisplayView() {
return textureView;
}
@Override
public Rect getPreviewFrame() {
return previewFrame;
}
@Override
public void takePicture(OnTakePictureCallback callback) {
this.onTakePictureCallback = callback;
// 拍照第一步,对焦
lockFocus();
}
@Override
public void setPermissionCallback(PermissionCallback callback) {
this.permissionCallback = callback;
}
@Override
public void setDisplayOrientation(@CameraView.Orientation int displayOrientation) {
this.orientation = displayOrientation / 90;
}
@Override
public void refreshPermission() {
openCamera(textureView.getWidth(), textureView.getHeight());
}
@Override
public void setFlashMode(@FlashMode int flashMode) {
if (this.flashMode == flashMode) {
return;
}
this.flashMode = flashMode;
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
updateFlashMode(flashMode, previewRequestBuilder);
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getFlashMode() {
return flashMode;
}
public Camera2Control(Context activity) {
this.context = activity;
textureView = new TextureView(activity);
}
private final TextureView.SurfaceTextureListener surfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
previewFrame.left = 0;
previewFrame.top = 0;
previewFrame.right = width;
previewFrame.bottom = height;
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
stop();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
private void openCamera(int width, int height) {
// 6.0+的系统需要检查系统权限 。
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
return;
}
setUpCameraOutputs(width, height);
configureTransform(width, height);
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
if (!cameraLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(cameraId, deviceStateCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
private final CameraDevice.StateCallback deviceStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
cameraLock.release();
Camera2Control.this.cameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
cameraLock.release();
cameraDevice.close();
Camera2Control.this.cameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
cameraLock.release();
cameraDevice.close();
Camera2Control.this.cameraDevice = null;
}
};
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface surface = new Surface(texture);
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
updateFlashMode(this.flashMode, previewRequestBuilder);
cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == cameraDevice) {
return;
}
captureSession = cameraCaptureSession;
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(previewRequest,
captureCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
// TODO
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final ImageReader.OnImageAvailableListener onImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
if (onTakePictureCallback != null) {
Image image = reader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
onTakePictureCallback.onPictureTaken(bytes);
}
}
};
private CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (state) {
case STATE_PREVIEW: {
break;
}
case STATE_WAITING_FOR_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
captureStillPicture();
return;
}
switch (afState) {
case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null
|| aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
captureStillPicture();
} else {
runPreCaptureSequence();
}
break;
}
default:
captureStillPicture();
}
break;
}
case STATE_WAITING_FOR_CAPTURE: {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null
|| aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE
|| aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
state = STATE_CAPTURING;
} else {
if (aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
captureStillPicture();
}
}
break;
}
case STATE_CAPTURING: {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
captureStillPicture();
}
break;
}
default:
break;
}
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
process(result);
}
};
private Size getOptimalSize(Size[] choices, int textureViewWidth,
int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
List<Size> bigEnough = new ArrayList<>();
List<Size> notBigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight
&& option.getHeight() == option.getWidth() * h / w) {
if (option.getWidth() >= textureViewWidth
&& option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, sizeComparator);
}
for (Size option : choices) {
if (option.getWidth() >= maxWidth && option.getHeight() >= maxHeight) {
return option;
}
}
if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, sizeComparator);
}
return choices[0];
}
private Comparator<Size> sizeComparator = new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
}
};
private void requestCameraPermission() {
if (permissionCallback != null) {
permissionCallback.onRequestPermission();
}
}
@SuppressWarnings("SuspiciousNameCombination")
private void setUpCameraOutputs(int width, int height) {
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics =
manager.getCameraCharacteristics(cameraId);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// int preferredPictureSize = (int) (Math.max(textureView.getWidth(), textureView
// .getHeight()) * 1.5);
// preferredPictureSize = Math.min(preferredPictureSize, 2560);
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point screenSize = new Point();
windowManager.getDefaultDisplay().getSize(screenSize);
int maxImageSize = Math.max(MAX_PREVIEW_SIZE, screenSize.y * 3 / 2);
Size size = getOptimalSize(map.getOutputSizes(ImageFormat.JPEG), textureView.getWidth(),
textureView.getHeight(), maxImageSize, maxImageSize, new Size(4, 3));
imageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
ImageFormat.JPEG, 1);
imageReader.setOnImageAvailableListener(
onImageAvailableListener, backgroundHandler);
int displayRotation = orientation;
// noinspection ConstantConditions
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
boolean swappedDimensions = false;
switch (displayRotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_180:
if (sensorOrientation == 90 || sensorOrientation == 270) {
swappedDimensions = true;
}
break;
case Surface.ROTATION_90:
case Surface.ROTATION_270:
if (sensorOrientation == 0 || sensorOrientation == 180) {
swappedDimensions = true;
}
break;
default:
}
int rotatedPreviewWidth = width;
int rotatedPreviewHeight = height;
int maxPreviewWidth = screenSize.x;
int maxPreviewHeight = screenSize.y;
if (swappedDimensions) {
rotatedPreviewWidth = height;
rotatedPreviewHeight = width;
maxPreviewWidth = screenSize.y;
maxPreviewHeight = screenSize.x;
}
maxPreviewWidth = Math.min(maxPreviewWidth, MAX_PREVIEW_WIDTH);
maxPreviewHeight = Math.min(maxPreviewHeight, MAX_PREVIEW_HEIGHT);
previewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, size);
this.cameraId = cameraId;
return;
}
} catch (CameraAccessException | NullPointerException e) {
e.printStackTrace();
}
}
private void closeCamera() {
try {
cameraLock.acquire();
if (null != captureSession) {
captureSession.close();
captureSession = null;
}
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != imageReader) {
imageReader.close();
imageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
cameraLock.release();
}
}
private void startBackgroundThread() {
backgroundThread = new HandlerThread("ocr_camera");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
private void stopBackgroundThread() {
if (backgroundThread != null) {
backgroundThread.quitSafely();
backgroundThread = null;
backgroundHandler = null;
}
}
private void configureTransform(int viewWidth, int viewHeight) {
if (null == textureView || null == previewSize || null == context) {
return;
}
int rotation = orientation;
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / previewSize.getHeight(),
(float) viewWidth / previewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
textureView.setTransform(matrix);
}
// 拍照前,先对焦
private void lockFocus() {
if (captureSession != null && state == STATE_PREVIEW) {
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
state = STATE_WAITING_FOR_LOCK;
captureSession.capture(previewRequestBuilder.build(), captureCallback,
backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
private void runPreCaptureSequence() {
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
state = STATE_WAITING_FOR_CAPTURE;
captureSession.capture(previewRequestBuilder.build(), captureCallback,
backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// 拍照session
private void captureStillPicture() {
try {
if (null == context || null == cameraDevice) {
return;
}
final CaptureRequest.Builder captureBuilder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(orientation));
updateFlashMode(this.flashMode, captureBuilder);
CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
unlockFocus();
}
@Override
public void onCaptureFailed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureFailure failure) {
super.onCaptureFailed(session, request, failure);
}
};
// 停止预览
captureSession.stopRepeating();
captureSession.capture(captureBuilder.build(), captureCallback, backgroundHandler);
state = STATE_PICTURE_TAKEN;
// unlockFocus();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private int getOrientation(int rotation) {
return (ORIENTATIONS.get(rotation) + sensorOrientation + 270) % 360;
}
// 停止对焦
private void unlockFocus() {
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
captureSession.capture(previewRequestBuilder.build(), captureCallback,
backgroundHandler);
state = STATE_PREVIEW;
// 预览
captureSession.setRepeatingRequest(previewRequest, captureCallback,
backgroundHandler);
textureView.setSurfaceTextureListener(surfaceTextureListener);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void updateFlashMode(@FlashMode int flashMode, CaptureRequest.Builder builder) {
switch (flashMode) {
case FLASH_MODE_TORCH:
builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
break;
case FLASH_MODE_OFF:
builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
break;
case ICameraControl.FLASH_MODE_AUTO:
default:
builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
break;
}
}
}
@@ -0,0 +1,516 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import com.baidu.idcardquality.IDcardQualityProcess;
import com.baidu.ocr.ui.R;
import com.baidu.ocr.ui.crop.CropView;
import com.baidu.ocr.ui.crop.FrameOverlayView;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.view.Surface;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
public class CameraActivity extends Activity {
public static final String KEY_OUTPUT_FILE_PATH = "outputFilePath";
public static final String KEY_CONTENT_TYPE = "contentType";
public static final String KEY_NATIVE_TOKEN = "nativeToken";
public static final String KEY_NATIVE_ENABLE = "nativeEnable";
public static final String KEY_NATIVE_MANUAL = "nativeEnableManual";
public static final String CONTENT_TYPE_GENERAL = "general";
public static final String CONTENT_TYPE_ID_CARD_FRONT = "IDCardFront";
public static final String CONTENT_TYPE_ID_CARD_BACK = "IDCardBack";
public static final String CONTENT_TYPE_BANK_CARD = "bankCard";
public static final String CONTENT_TYPE_PASSPORT = "passport";
private static final int REQUEST_CODE_PICK_IMAGE = 100;
private static final int PERMISSIONS_REQUEST_CAMERA = 800;
private static final int PERMISSIONS_EXTERNAL_STORAGE = 801;
private File outputFile;
private String contentType;
private Handler handler = new Handler();
private boolean isNativeEnable;
private boolean isNativeManual;
private OCRCameraLayout takePictureContainer;
private OCRCameraLayout cropContainer;
private OCRCameraLayout confirmResultContainer;
private ImageView lightButton;
private CameraView cameraView;
private ImageView displayImageView;
private CropView cropView;
private FrameOverlayView overlayView;
private MaskView cropMaskView;
private ImageView takePhotoBtn;
private PermissionCallback permissionCallback = new PermissionCallback() {
@Override
public boolean onRequestPermission() {
ActivityCompat.requestPermissions(CameraActivity.this,
new String[] {Manifest.permission.CAMERA},
PERMISSIONS_REQUEST_CAMERA);
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bd_ocr_activity_camera);
takePictureContainer = (OCRCameraLayout) findViewById(R.id.take_picture_container);
confirmResultContainer = (OCRCameraLayout) findViewById(R.id.confirm_result_container);
cameraView = (CameraView) findViewById(R.id.camera_view);
cameraView.getCameraControl().setPermissionCallback(permissionCallback);
lightButton = (ImageView) findViewById(R.id.light_button);
lightButton.setOnClickListener(lightButtonOnClickListener);
takePhotoBtn = (ImageView) findViewById(R.id.take_photo_button);
findViewById(R.id.album_button).setOnClickListener(albumButtonOnClickListener);
takePhotoBtn.setOnClickListener(takeButtonOnClickListener);
// confirm result;
displayImageView = (ImageView) findViewById(R.id.display_image_view);
confirmResultContainer.findViewById(R.id.confirm_button).setOnClickListener(confirmButtonOnClickListener);
confirmResultContainer.findViewById(R.id.cancel_button).setOnClickListener(confirmCancelButtonOnClickListener);
findViewById(R.id.rotate_button).setOnClickListener(rotateButtonOnClickListener);
cropView = (CropView) findViewById(R.id.crop_view);
cropContainer = (OCRCameraLayout) findViewById(R.id.crop_container);
overlayView = (FrameOverlayView) findViewById(R.id.overlay_view);
cropContainer.findViewById(R.id.confirm_button).setOnClickListener(cropConfirmButtonListener);
cropMaskView = (MaskView) cropContainer.findViewById(R.id.crop_mask_view);
cropContainer.findViewById(R.id.cancel_button).setOnClickListener(cropCancelButtonListener);
setOrientation(getResources().getConfiguration());
initParams();
cameraView.setAutoPictureCallback(autoTakePictureCallback);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
cameraView.stop();
}
@Override
protected void onResume() {
super.onResume();
cameraView.start();
}
private void initParams() {
String outputPath = getIntent().getStringExtra(KEY_OUTPUT_FILE_PATH);
final String token = getIntent().getStringExtra(KEY_NATIVE_TOKEN);
isNativeEnable = getIntent().getBooleanExtra(KEY_NATIVE_ENABLE, true);
isNativeManual = getIntent().getBooleanExtra(KEY_NATIVE_MANUAL, false);
if (token == null && !isNativeManual) {
isNativeEnable = false;
}
if (outputPath != null) {
outputFile = new File(outputPath);
}
contentType = getIntent().getStringExtra(KEY_CONTENT_TYPE);
if (contentType == null) {
contentType = CONTENT_TYPE_GENERAL;
}
int maskType;
switch (contentType) {
case CONTENT_TYPE_ID_CARD_FRONT:
maskType = MaskView.MASK_TYPE_ID_CARD_FRONT;
overlayView.setVisibility(View.INVISIBLE);
if (isNativeEnable) {
takePhotoBtn.setVisibility(View.INVISIBLE);
}
break;
case CONTENT_TYPE_ID_CARD_BACK:
maskType = MaskView.MASK_TYPE_ID_CARD_BACK;
overlayView.setVisibility(View.INVISIBLE);
if (isNativeEnable) {
takePhotoBtn.setVisibility(View.INVISIBLE);
}
break;
case CONTENT_TYPE_BANK_CARD:
maskType = MaskView.MASK_TYPE_BANK_CARD;
overlayView.setVisibility(View.INVISIBLE);
break;
case CONTENT_TYPE_PASSPORT:
maskType = MaskView.MASK_TYPE_PASSPORT;
overlayView.setVisibility(View.INVISIBLE);
break;
case CONTENT_TYPE_GENERAL:
default:
maskType = MaskView.MASK_TYPE_NONE;
cropMaskView.setVisibility(View.INVISIBLE);
break;
}
// 身份证本地能力初始化
if (maskType == MaskView.MASK_TYPE_ID_CARD_FRONT || maskType == MaskView.MASK_TYPE_ID_CARD_BACK) {
if (isNativeEnable && !isNativeManual) {
initNative(token);
}
}
cameraView.setEnableScan(isNativeEnable);
cameraView.setMaskType(maskType, this);
cropMaskView.setMaskType(maskType);
}
private void initNative(final String token) {
CameraNativeHelper.init(CameraActivity.this, token,
new CameraNativeHelper.CameraNativeInitCallback() {
@Override
public void onError(int errorCode, Throwable e) {
cameraView.setInitNativeStatus(errorCode);
}
});
}
private void showTakePicture() {
cameraView.getCameraControl().resume();
updateFlashMode();
takePictureContainer.setVisibility(View.VISIBLE);
confirmResultContainer.setVisibility(View.INVISIBLE);
cropContainer.setVisibility(View.INVISIBLE);
}
private void showCrop() {
cameraView.getCameraControl().pause();
updateFlashMode();
takePictureContainer.setVisibility(View.INVISIBLE);
confirmResultContainer.setVisibility(View.INVISIBLE);
cropContainer.setVisibility(View.VISIBLE);
}
private void showResultConfirm() {
cameraView.getCameraControl().pause();
updateFlashMode();
takePictureContainer.setVisibility(View.INVISIBLE);
confirmResultContainer.setVisibility(View.VISIBLE);
cropContainer.setVisibility(View.INVISIBLE);
}
// take photo;
private void updateFlashMode() {
int flashMode = cameraView.getCameraControl().getFlashMode();
if (flashMode == ICameraControl.FLASH_MODE_TORCH) {
lightButton.setImageResource(R.drawable.bd_ocr_light_on);
} else {
lightButton.setImageResource(R.drawable.bd_ocr_light_off);
}
}
private View.OnClickListener albumButtonOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ActivityCompat.requestPermissions(CameraActivity.this,
new String[] {Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSIONS_EXTERNAL_STORAGE);
return;
}
}
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE);
}
};
private View.OnClickListener lightButtonOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (cameraView.getCameraControl().getFlashMode() == ICameraControl.FLASH_MODE_OFF) {
cameraView.getCameraControl().setFlashMode(ICameraControl.FLASH_MODE_TORCH);
} else {
cameraView.getCameraControl().setFlashMode(ICameraControl.FLASH_MODE_OFF);
}
updateFlashMode();
}
};
private View.OnClickListener takeButtonOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
cameraView.takePicture(outputFile, takePictureCallback);
}
};
private CameraView.OnTakePictureCallback autoTakePictureCallback = new CameraView.OnTakePictureCallback() {
@Override
public void onPictureTaken(final Bitmap bitmap) {
CameraThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
bitmap.recycle();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, contentType);
setResult(Activity.RESULT_OK, intent);
finish();
}
});
}
};
private CameraView.OnTakePictureCallback takePictureCallback = new CameraView.OnTakePictureCallback() {
@Override
public void onPictureTaken(final Bitmap bitmap) {
handler.post(new Runnable() {
@Override
public void run() {
takePictureContainer.setVisibility(View.INVISIBLE);
if (cropMaskView.getMaskType() == MaskView.MASK_TYPE_NONE) {
cropView.setFilePath(outputFile.getAbsolutePath());
showCrop();
} else if (cropMaskView.getMaskType() == MaskView.MASK_TYPE_BANK_CARD) {
cropView.setFilePath(outputFile.getAbsolutePath());
cropMaskView.setVisibility(View.INVISIBLE);
overlayView.setVisibility(View.VISIBLE);
overlayView.setTypeWide();
showCrop();
} else {
displayImageView.setImageBitmap(bitmap);
showResultConfirm();
}
}
});
}
};
private View.OnClickListener cropCancelButtonListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// 释放 cropView中的bitmap;
cropView.setFilePath(null);
showTakePicture();
}
};
private View.OnClickListener cropConfirmButtonListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int maskType = cropMaskView.getMaskType();
Rect rect;
switch (maskType) {
case MaskView.MASK_TYPE_BANK_CARD:
case MaskView.MASK_TYPE_ID_CARD_BACK:
case MaskView.MASK_TYPE_ID_CARD_FRONT:
rect = cropMaskView.getFrameRect();
break;
case MaskView.MASK_TYPE_NONE:
default:
rect = overlayView.getFrameRect();
break;
}
Bitmap cropped = cropView.crop(rect);
displayImageView.setImageBitmap(cropped);
cropAndConfirm();
}
};
private void cropAndConfirm() {
cameraView.getCameraControl().pause();
updateFlashMode();
doConfirmResult();
}
private void doConfirmResult() {
CameraThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
Bitmap bitmap = ((BitmapDrawable) displayImageView.getDrawable()).getBitmap();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, contentType);
setResult(Activity.RESULT_OK, intent);
finish();
}
});
}
private View.OnClickListener confirmButtonOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
doConfirmResult();
}
};
private View.OnClickListener confirmCancelButtonOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
displayImageView.setImageBitmap(null);
showTakePicture();
}
};
private View.OnClickListener rotateButtonOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
cropView.rotate(90);
}
};
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = null;
try {
cursor = getContentResolver().query(contentURI, null, null, null, null);
} catch (Throwable e) {
e.printStackTrace();
}
if (cursor == null) {
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setOrientation(newConfig);
}
private void setOrientation(Configuration newConfig) {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int orientation;
int cameraViewOrientation = CameraView.ORIENTATION_PORTRAIT;
switch (newConfig.orientation) {
case Configuration.ORIENTATION_PORTRAIT:
cameraViewOrientation = CameraView.ORIENTATION_PORTRAIT;
orientation = OCRCameraLayout.ORIENTATION_PORTRAIT;
break;
case Configuration.ORIENTATION_LANDSCAPE:
orientation = OCRCameraLayout.ORIENTATION_HORIZONTAL;
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
cameraViewOrientation = CameraView.ORIENTATION_HORIZONTAL;
} else {
cameraViewOrientation = CameraView.ORIENTATION_INVERT;
}
break;
default:
orientation = OCRCameraLayout.ORIENTATION_PORTRAIT;
cameraView.setOrientation(CameraView.ORIENTATION_PORTRAIT);
break;
}
takePictureContainer.setOrientation(orientation);
cameraView.setOrientation(cameraViewOrientation);
cropContainer.setOrientation(orientation);
confirmResultContainer.setOrientation(orientation);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_PICK_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
cropView.setFilePath(getRealPathFromURI(uri));
showCrop();
} else {
cameraView.getCameraControl().resume();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSIONS_REQUEST_CAMERA: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
cameraView.getCameraControl().refreshPermission();
} else {
Toast.makeText(getApplicationContext(), R.string.camera_permission_required, Toast.LENGTH_LONG)
.show();
}
break;
}
case PERMISSIONS_EXTERNAL_STORAGE:
default:
break;
}
}
/**
* 做一些收尾工作
*
*/
private void doClear() {
CameraThreadPool.cancelAutoFocusTimer();
if (isNativeEnable && !isNativeManual) {
IDcardQualityProcess.getInstance().releaseModel();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
this.doClear();
}
}
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import android.content.Context;
import com.baidu.idcardquality.IDcardQualityProcess;
/**
* Created by ruanshimin on 2018/1/23.
*/
public class CameraNativeHelper {
public interface CameraNativeInitCallback {
/**
* 加载本地库异常回调
*
* @param errorCode 错误代码
* @param e 如果加载so异常则会有异常对象传入
*/
void onError(int errorCode, Throwable e);
}
public static void init(final Context ctx, final String token, final CameraNativeInitCallback cb) {
CameraThreadPool.execute(new Runnable() {
@Override
public void run() {
int status;
// 加载本地so失败, 异常返回getloadSoException
if (IDcardQualityProcess.getLoadSoException() != null) {
status = CameraView.NATIVE_SOLOAD_FAIL;
cb.onError(status, IDcardQualityProcess.getLoadSoException());
return;
}
// 授权状态
int authStatus = IDcardQualityProcess.init(token);
if (authStatus != 0) {
cb.onError(CameraView.NATIVE_AUTH_FAIL, null);
return;
}
// 加载模型状态
int initModelStatus = IDcardQualityProcess.getInstance()
.idcardQualityInit(ctx.getAssets(),
"models");
if (initModelStatus != 0) {
cb.onError(CameraView.NATIVE_INIT_FAIL, null);
}
}
});
}
public static void release() {
IDcardQualityProcess.getInstance().releaseModel();
}
}
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CameraThreadPool {
static Timer timerFocus = null;
/*
* 对焦频率
*/
static final long cameraScanInterval = 2000;
/*
* 线程池大小
*/
private static int poolCount = Runtime.getRuntime().availableProcessors();
private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(poolCount);
/**
* 给线程池添加任务
* @param runnable 任务
*/
public static void execute(Runnable runnable) {
fixedThreadPool.execute(runnable);
}
/**
* 创建一个定时对焦的timer任务
* @param runnable 对焦代码
* @return Timer Timer对象,用来终止自动对焦
*/
public static Timer createAutoFocusTimerTask(final Runnable runnable) {
if (timerFocus != null) {
return timerFocus;
}
timerFocus = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
runnable.run();
}
};
timerFocus.scheduleAtFixedRate(task, 0, cameraScanInterval);
return timerFocus;
}
/**
* 终止自动对焦任务,实际调用了cancel方法并且清空对象
* 但是无法终止执行中的任务,需额外处理
*
*/
public static void cancelAutoFocusTimer() {
if (timerFocus != null) {
timerFocus.cancel();
timerFocus = null;
}
}
}
@@ -0,0 +1,650 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import com.baidu.idcardquality.IDcardQualityProcess;
import com.baidu.ocr.ui.R;
import com.baidu.ocr.ui.util.DimensionUtil;
import com.baidu.ocr.ui.util.ImageUtil;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.ImageReader;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.IntDef;
/**
* 负责,相机的管理。同时提供,裁剪遮罩功能。
*/
public class CameraView extends FrameLayout {
private int maskType;
/**
* 照相回调
*/
interface OnTakePictureCallback {
void onPictureTaken(Bitmap bitmap);
}
/**
* 垂直方向 {@link #setOrientation(int)}
*/
public static final int ORIENTATION_PORTRAIT = 0;
/**
* 水平方向 {@link #setOrientation(int)}
*/
public static final int ORIENTATION_HORIZONTAL = 90;
/**
* 水平翻转方向 {@link #setOrientation(int)}
*/
public static final int ORIENTATION_INVERT = 270;
/**
* 本地模型授权,加载成功
*/
public static final int NATIVE_AUTH_INIT_SUCCESS = 0;
/**
* 本地模型授权,缺少SO
*/
public static final int NATIVE_SOLOAD_FAIL = 10;
/**
* 本地模型授权,授权失败,token异常
*/
public static final int NATIVE_AUTH_FAIL = 11;
/**
* 本地模型授权,模型加载失败
*/
public static final int NATIVE_INIT_FAIL = 12;
/**
* 是否已经通过本地质量控制扫描
*/
private final int SCAN_SUCCESS = 0;
public void setInitNativeStatus(int initNativeStatus) {
this.initNativeStatus = initNativeStatus;
}
/**
* 本地检测初始化,模型加载标识
*/
private int initNativeStatus = NATIVE_AUTH_INIT_SUCCESS;
@IntDef({ORIENTATION_PORTRAIT, ORIENTATION_HORIZONTAL, ORIENTATION_INVERT})
public @interface Orientation {
}
private CameraViewTakePictureCallback cameraViewTakePictureCallback = new CameraViewTakePictureCallback();
private ICameraControl cameraControl;
/**
* 相机预览View
*/
private View displayView;
/**
* 身份证,银行卡,等裁剪用的遮罩
*/
private MaskView maskView;
/**
* 用于显示提示证 "请对齐身份证正面" 之类的背景
*/
private ImageView hintView;
/**
* 用于显示提示证 "请对齐身份证正面" 之类的文字
*/
private TextView hintViewText;
/**
* 提示文案容器
*/
private LinearLayout hintViewTextWrapper;
/**
* 是否是本地质量控制扫描
*/
private boolean isEnableScan;
public void setEnableScan(boolean enableScan) {
isEnableScan = enableScan;
}
/**
* UI线程的handler
*/
Handler uiHandler = new Handler(Looper.getMainLooper());
public ICameraControl getCameraControl() {
return cameraControl;
}
public void setOrientation(@Orientation int orientation) {
cameraControl.setDisplayOrientation(orientation);
}
public CameraView(Context context) {
super(context);
init();
}
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void start() {
cameraControl.start();
setKeepScreenOn(true);
}
public void stop() {
cameraControl.stop();
setKeepScreenOn(false);
}
public void takePicture(final File file, final OnTakePictureCallback callback) {
cameraViewTakePictureCallback.file = file;
cameraViewTakePictureCallback.callback = callback;
cameraControl.takePicture(cameraViewTakePictureCallback);
}
private OnTakePictureCallback autoPictureCallback;
public void setAutoPictureCallback(OnTakePictureCallback callback) {
autoPictureCallback = callback;
}
public void setMaskType(@MaskView.MaskType int maskType, final Context ctx) {
maskView.setMaskType(maskType);
maskView.setVisibility(VISIBLE);
hintView.setVisibility(VISIBLE);
int hintResourceId = R.drawable.bd_ocr_hint_align_id_card;
this.maskType = maskType;
boolean isNeedSetImage = true;
switch (maskType) {
case MaskView.MASK_TYPE_ID_CARD_FRONT:
hintResourceId = R.drawable.bd_ocr_round_corner;
isNeedSetImage = false;
break;
case MaskView.MASK_TYPE_ID_CARD_BACK:
isNeedSetImage = false;
hintResourceId = R.drawable.bd_ocr_round_corner;
break;
case MaskView.MASK_TYPE_BANK_CARD:
hintResourceId = R.drawable.bd_ocr_hint_align_bank_card;
break;
case MaskView.MASK_TYPE_PASSPORT:
hintView.setVisibility(INVISIBLE);
break;
case MaskView.MASK_TYPE_NONE:
default:
maskView.setVisibility(INVISIBLE);
hintView.setVisibility(INVISIBLE);
break;
}
if (isNeedSetImage) {
hintView.setImageResource(hintResourceId);
hintViewTextWrapper.setVisibility(INVISIBLE);
}
if (maskType == MaskView.MASK_TYPE_ID_CARD_FRONT && isEnableScan) {
cameraControl.setDetectCallback(new ICameraControl.OnDetectPictureCallback() {
@Override
public int onDetect(byte[] data, int rotation) {
return detect(data, rotation);
}
});
}
if (maskType == MaskView.MASK_TYPE_ID_CARD_BACK && isEnableScan) {
cameraControl.setDetectCallback(new ICameraControl.OnDetectPictureCallback() {
@Override
public int onDetect(byte[] data, int rotation) {
return detect(data, rotation);
}
});
}
}
private int detect(byte[] data, final int rotation) {
if (initNativeStatus != NATIVE_AUTH_INIT_SUCCESS) {
showTipMessage(initNativeStatus);
return 1;
}
// 扫描成功阻止多余的操作
if (cameraControl.getAbortingScan().get()) {
return 0;
}
Rect previewFrame = cameraControl.getPreviewFrame();
if (maskView.getWidth() == 0 || maskView.getHeight() == 0
|| previewFrame.width() == 0 || previewFrame.height() == 0) {
return 0;
}
// BitmapRegionDecoder不会将整个图片加载到内存。
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(data, 0, data.length, true);
} catch (IOException e) {
e.printStackTrace();
}
int width = rotation % 180 == 0 ? decoder.getWidth() : decoder.getHeight();
int height = rotation % 180 == 0 ? decoder.getHeight() : decoder.getWidth();
Rect frameRect = maskView.getFrameRectExtend();
int left = width * frameRect.left / maskView.getWidth();
int top = height * frameRect.top / maskView.getHeight();
int right = width * frameRect.right / maskView.getWidth();
int bottom = height * frameRect.bottom / maskView.getHeight();
// 高度大于图片
if (previewFrame.top < 0) {
// 宽度对齐。
int adjustedPreviewHeight = previewFrame.height() * getWidth() / previewFrame.width();
int topInFrame = ((adjustedPreviewHeight - frameRect.height()) / 2)
* getWidth() / previewFrame.width();
int bottomInFrame = ((adjustedPreviewHeight + frameRect.height()) / 2) * getWidth()
/ previewFrame.width();
// 等比例投射到照片当中。
top = topInFrame * height / previewFrame.height();
bottom = bottomInFrame * height / previewFrame.height();
} else {
// 宽度大于图片
if (previewFrame.left < 0) {
// 高度对齐
int adjustedPreviewWidth = previewFrame.width() * getHeight() / previewFrame.height();
int leftInFrame = ((adjustedPreviewWidth - maskView.getFrameRect().width()) / 2) * getHeight()
/ previewFrame.height();
int rightInFrame = ((adjustedPreviewWidth + maskView.getFrameRect().width()) / 2) * getHeight()
/ previewFrame.height();
// 等比例投射到照片当中。
left = leftInFrame * width / previewFrame.width();
right = rightInFrame * width / previewFrame.width();
}
}
Rect region = new Rect();
region.left = left;
region.top = top;
region.right = right;
region.bottom = bottom;
// 90度或者270度旋转
if (rotation % 180 == 90) {
int x = decoder.getWidth() / 2;
int y = decoder.getHeight() / 2;
int rotatedWidth = region.height();
int rotated = region.width();
// 计算,裁剪框旋转后的坐标
region.left = x - rotatedWidth / 2;
region.top = y - rotated / 2;
region.right = x + rotatedWidth / 2;
region.bottom = y + rotated / 2;
region.sort();
}
BitmapFactory.Options options = new BitmapFactory.Options();
// 最大图片大小。
int maxPreviewImageSize = 2560;
int size = Math.min(decoder.getWidth(), decoder.getHeight());
size = Math.min(size, maxPreviewImageSize);
options.inSampleSize = ImageUtil.calculateInSampleSize(options, size, size);
options.inScaled = true;
options.inDensity = Math.max(options.outWidth, options.outHeight);
options.inTargetDensity = size;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = decoder.decodeRegion(region, options);
if (rotation != 0) {
// 只能是裁剪完之后再旋转了。有没有别的更好的方案呢?
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
Bitmap rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
if (bitmap != rotatedBitmap) {
// 有时候 createBitmap会复用对象
bitmap.recycle();
}
bitmap = rotatedBitmap;
}
final int status;
// 调用本地质量控制请求
switch (maskType) {
case MaskView.MASK_TYPE_ID_CARD_FRONT:
status = IDcardQualityProcess.getInstance().idcardQualityDetectionImg(bitmap, true);
break;
case MaskView.MASK_TYPE_ID_CARD_BACK:
status = IDcardQualityProcess.getInstance().idcardQualityDetectionImg(bitmap, false);
break;
default:
status = 1;
}
// 当有某个扫描处理线程调用成功后,阻止其他线程继续调用本地控制代码
if (status == SCAN_SUCCESS) {
// 扫描成功阻止多线程同时回调
if (!cameraControl.getAbortingScan().compareAndSet(false, true)) {
bitmap.recycle();
return 0;
}
autoPictureCallback.onPictureTaken(bitmap);
}
showTipMessage(status);
return status;
}
private void showTipMessage(final int status) {
// 提示tip文字变化
uiHandler.post(new Runnable() {
@Override
public void run() {
if (status == 0) {
hintViewText.setVisibility(View.INVISIBLE);
} else if (!cameraControl.getAbortingScan().get()) {
hintViewText.setVisibility(View.VISIBLE);
hintViewText.setText(getScanMessage(status));
}
}
});
}
private String getScanMessage(int status) {
String message;
switch (status) {
case 0:
message = "";
break;
case 2:
message = "身份证模糊,请重新尝试";
break;
case 3:
message = "身份证反光,请重新尝试";
break;
case 4:
message = "请将身份证前后反转再进行识别";
break;
case 5:
message = "请拿稳镜头和身份证";
break;
case 6:
message = "请将镜头靠近身份证";
break;
case 7:
message = "请将身份证完整置于取景框内";
break;
case NATIVE_AUTH_FAIL:
message = "本地质量控制授权失败";
break;
case NATIVE_INIT_FAIL:
message = "本地模型加载失败";
break;
case NATIVE_SOLOAD_FAIL:
message = "本地SO库加载失败";
break;
case 1:
default:
message = "请将身份证置于取景框内";
}
return message;
}
private void init() {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// cameraControl = new Camera2Control(getContext());
// } else {
//
// }
cameraControl = new Camera1Control(getContext());
displayView = cameraControl.getDisplayView();
addView(displayView);
maskView = new MaskView(getContext());
addView(maskView);
hintView = new ImageView(getContext());
addView(hintView);
hintViewTextWrapper = new LinearLayout(getContext());
hintViewTextWrapper.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
DimensionUtil.dpToPx(25));
lp.gravity = Gravity.CENTER;
hintViewText = new TextView(getContext());
hintViewText.setBackgroundResource(R.drawable.bd_ocr_round_corner);
hintViewText.setAlpha(0.5f);
hintViewText.setPadding(DimensionUtil.dpToPx(10), 0, DimensionUtil.dpToPx(10), 0);
hintViewTextWrapper.addView(hintViewText, lp);
hintViewText.setGravity(Gravity.CENTER);
hintViewText.setTextColor(Color.WHITE);
hintViewText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
hintViewText.setText(getScanMessage(-1));
addView(hintViewTextWrapper, lp);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
displayView.layout(left, 0, right, bottom - top);
maskView.layout(left, 0, right, bottom - top);
int hintViewWidth = DimensionUtil.dpToPx(250);
int hintViewHeight = DimensionUtil.dpToPx(25);
int hintViewLeft = (getWidth() - hintViewWidth) / 2;
int hintViewTop = maskView.getFrameRect().bottom + DimensionUtil.dpToPx(16);
hintViewTextWrapper.layout(hintViewLeft, hintViewTop,
hintViewLeft + hintViewWidth, hintViewTop + hintViewHeight);
hintView.layout(hintViewLeft, hintViewTop,
hintViewLeft + hintViewWidth, hintViewTop + hintViewHeight);
}
/**
* 拍摄后的照片。需要进行裁剪。有些手机(比如三星)不会对照片数据进行旋转,而是将旋转角度写入EXIF信息当中,
* 所以需要做旋转处理。
*
* @param outputFile 写入照片的文件。
* @param data 原始照片数据。
* @param rotation 照片exif中的旋转角度。
*
* @return 裁剪好的bitmap。
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
private Bitmap crop(File outputFile, byte[] data, int rotation) {
try {
Rect previewFrame = cameraControl.getPreviewFrame();
if (maskView.getWidth() == 0 || maskView.getHeight() == 0
|| previewFrame.width() == 0 || previewFrame.height() == 0) {
return null;
}
// BitmapRegionDecoder不会将整个图片加载到内存。
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(data, 0, data.length, true);
int width = rotation % 180 == 0 ? decoder.getWidth() : decoder.getHeight();
int height = rotation % 180 == 0 ? decoder.getHeight() : decoder.getWidth();
Rect frameRect = maskView.getFrameRect();
int left = width * frameRect.left / maskView.getWidth();
int top = height * frameRect.top / maskView.getHeight();
int right = width * frameRect.right / maskView.getWidth();
int bottom = height * frameRect.bottom / maskView.getHeight();
// 高度大于图片
if (previewFrame.top < 0) {
// 宽度对齐。
int adjustedPreviewHeight = previewFrame.height() * getWidth() / previewFrame.width();
int topInFrame = ((adjustedPreviewHeight - frameRect.height()) / 2)
* getWidth() / previewFrame.width();
int bottomInFrame = ((adjustedPreviewHeight + frameRect.height()) / 2) * getWidth()
/ previewFrame.width();
// 等比例投射到照片当中。
top = topInFrame * height / previewFrame.height();
bottom = bottomInFrame * height / previewFrame.height();
} else {
// 宽度大于图片
if (previewFrame.left < 0) {
// 高度对齐
int adjustedPreviewWidth = previewFrame.width() * getHeight() / previewFrame.height();
int leftInFrame = ((adjustedPreviewWidth - maskView.getFrameRect().width()) / 2) * getHeight()
/ previewFrame.height();
int rightInFrame = ((adjustedPreviewWidth + maskView.getFrameRect().width()) / 2) * getHeight()
/ previewFrame.height();
// 等比例投射到照片当中。
left = leftInFrame * width / previewFrame.width();
right = rightInFrame * width / previewFrame.width();
}
}
Rect region = new Rect();
region.left = left;
region.top = top;
region.right = right;
region.bottom = bottom;
// 90度或者270度旋转
if (rotation % 180 == 90) {
int x = decoder.getWidth() / 2;
int y = decoder.getHeight() / 2;
int rotatedWidth = region.height();
int rotated = region.width();
// 计算,裁剪框旋转后的坐标
region.left = x - rotatedWidth / 2;
region.top = y - rotated / 2;
region.right = x + rotatedWidth / 2;
region.bottom = y + rotated / 2;
region.sort();
}
BitmapFactory.Options options = new BitmapFactory.Options();
// 最大图片大小。
int maxPreviewImageSize = 2560;
int size = Math.min(decoder.getWidth(), decoder.getHeight());
size = Math.min(size, maxPreviewImageSize);
options.inSampleSize = ImageUtil.calculateInSampleSize(options, size, size);
options.inScaled = true;
options.inDensity = Math.max(options.outWidth, options.outHeight);
options.inTargetDensity = size;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = decoder.decodeRegion(region, options);
if (rotation != 0) {
// 只能是裁剪完之后再旋转了。有没有别的更好的方案呢?
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
Bitmap rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
if (bitmap != rotatedBitmap) {
// 有时候 createBitmap会复用对象
bitmap.recycle();
}
bitmap = rotatedBitmap;
}
try {
if (!outputFile.exists()) {
outputFile.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void release() {
IDcardQualityProcess.getInstance().releaseModel();
}
private class CameraViewTakePictureCallback implements ICameraControl.OnTakePictureCallback {
private File file;
private OnTakePictureCallback callback;
@Override
public void onPictureTaken(final byte[] data) {
CameraThreadPool.execute(new Runnable() {
@Override
public void run() {
final int rotation = ImageUtil.getOrientation(data);
Bitmap bitmap = crop(file, data, rotation);
callback.onPictureTaken(bitmap);
}
});
}
}
}
@@ -0,0 +1,121 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.IntDef;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Android 5.0 相机的API发生很大的变化。些类屏蔽掉了 api的变化。相机的操作和功能,抽象剥离出来。
*/
public interface ICameraControl {
/**
* 闪光灯关 {@link #setFlashMode(int)}
*/
int FLASH_MODE_OFF = 0;
/**
* 闪光灯开 {@link #setFlashMode(int)}
*/
int FLASH_MODE_TORCH = 1;
/**
* 闪光灯自动 {@link #setFlashMode(int)}
*/
int FLASH_MODE_AUTO = 2;
@IntDef({FLASH_MODE_TORCH, FLASH_MODE_OFF, FLASH_MODE_AUTO})
@interface FlashMode {
}
/**
* 照相回调。
*/
interface OnTakePictureCallback {
void onPictureTaken(byte[] data);
}
/**
* 设置本地质量控制回调,如果不设置则视为不扫描调用本地质量控制代码。
*/
void setDetectCallback(OnDetectPictureCallback callback);
/**
* 预览回调
*/
interface OnDetectPictureCallback {
int onDetect(byte[] data, int rotation);
}
/**
* 打开相机。
*/
void start();
/**
* 关闭相机
*/
void stop();
void pause();
void resume();
/**
* 相机对应的预览视图。
* @return 预览视图
*/
View getDisplayView();
/**
* 看到的预览可能不是照片的全部。返回预览视图的全貌。
* @return 预览视图frame;
*/
Rect getPreviewFrame();
/**
* 拍照。结果在回调中获取。
* @param callback 拍照结果回调
*/
void takePicture(OnTakePictureCallback callback);
/**
* 设置权限回调,当手机没有拍照权限时,可在回调中获取。
* @param callback 权限回调
*/
void setPermissionCallback(PermissionCallback callback);
/**
* 设置水平方向
* @param displayOrientation 参数值见 {@link com.baidu.ocr.ui.camera.CameraView.Orientation}
*/
void setDisplayOrientation(@CameraView.Orientation int displayOrientation);
/**
* 获取到拍照权限时,调用些函数以继续。
*/
void refreshPermission();
/**
* 获取已经扫描成功,处理中
*/
AtomicBoolean getAbortingScan();
/**
* 设置闪光灯状态。
* @param flashMode {@link #FLASH_MODE_TORCH,#FLASH_MODE_OFF,#FLASH_MODE_AUTO}
*/
void setFlashMode(@FlashMode int flashMode);
/**
* 获取当前闪光灯状态
* @return 当前闪光灯状态 参见 {@link #setFlashMode(int)}
*/
@FlashMode
int getFlashMode();
}
@@ -0,0 +1,279 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import java.io.File;
import com.baidu.ocr.ui.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.IntDef;
import androidx.annotation.RequiresApi;
import androidx.core.content.res.ResourcesCompat;
@SuppressWarnings("unused")
public class MaskView extends View {
public static final int MASK_TYPE_NONE = 0;
public static final int MASK_TYPE_ID_CARD_FRONT = 1;
public static final int MASK_TYPE_ID_CARD_BACK = 2;
public static final int MASK_TYPE_BANK_CARD = 11;
public static final int MASK_TYPE_PASSPORT = 21;
@IntDef({MASK_TYPE_NONE, MASK_TYPE_ID_CARD_FRONT, MASK_TYPE_ID_CARD_BACK, MASK_TYPE_BANK_CARD,
MASK_TYPE_PASSPORT})
@interface MaskType {
}
public void setLineColor(int lineColor) {
this.lineColor = lineColor;
}
public void setMaskColor(int maskColor) {
this.maskColor = maskColor;
}
private int lineColor = Color.WHITE;
private int maskType = MASK_TYPE_ID_CARD_FRONT;
private int maskColor = Color.argb(100, 0, 0, 0);
private Paint eraser = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint pen = new Paint(Paint.ANTI_ALIAS_FLAG);
private Rect frame = new Rect();
private Rect framePassport = new Rect();
private Drawable locatorDrawable;
public Rect getFrameRect() {
if (maskType == MASK_TYPE_NONE) {
return new Rect(0, 0, getWidth(), getHeight());
} else if (maskType == MASK_TYPE_PASSPORT) {
return new Rect(framePassport);
} else {
return new Rect(frame);
}
}
public Rect getFrameRectExtend() {
Rect rc = new Rect(frame);
int widthExtend = (int) ((frame.right - frame.left) * 0.02f);
int heightExtend = (int) ((frame.bottom - frame.top) * 0.02f);
rc.left -= widthExtend;
rc.right += widthExtend;
rc.top -= heightExtend;
rc.bottom += heightExtend;
return rc;
}
public void setMaskType(@MaskType int maskType) {
this.maskType = maskType;
switch (maskType) {
case MASK_TYPE_ID_CARD_FRONT:
locatorDrawable = ResourcesCompat.getDrawable(getResources(),
R.drawable.bd_ocr_id_card_locator_front, null);
break;
case MASK_TYPE_ID_CARD_BACK:
locatorDrawable = ResourcesCompat.getDrawable(getResources(),
R.drawable.bd_ocr_id_card_locator_back, null);
break;
case MASK_TYPE_PASSPORT:
locatorDrawable = ResourcesCompat.getDrawable(getResources(),
R.drawable.bd_ocr_passport_locator, null);
break;
case MASK_TYPE_BANK_CARD:
break;
case MASK_TYPE_NONE:
default:
break;
}
invalidate();
}
public int getMaskType() {
return maskType;
}
public void setOrientation(@CameraView.Orientation int orientation) {
}
public MaskView(Context context) {
super(context);
init();
}
public MaskView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
locatorDrawable = ResourcesCompat.getDrawable(getResources(), R.drawable.bd_ocr_id_card_locator_front, null);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w > 0 && h > 0) {
if (maskType != MASK_TYPE_PASSPORT) {
float ratio = h > w ? 0.9f : 0.72f;
int width = (int) (w * ratio);
int height = width * 400 / 620;
int left = (w - width) / 2;
int top = (h - height) / 2;
int right = width + left;
int bottom = height + top;
frame.left = left;
frame.top = top;
frame.right = right;
frame.bottom = bottom;
} else {
float ratio = 0.9f;
int width = (int) (w * ratio);
int height = width * 330 / 470;
int left = (w - width) / 2;
int top = (h - height) / 2;
int right = width + left;
int bottom = height + top;
framePassport.left = left;
framePassport.top = top;
framePassport.right = right;
framePassport.bottom = bottom;
}
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect frame = this.frame;
if (maskType == MASK_TYPE_PASSPORT) {
frame = framePassport;
}
int width = frame.width();
int height = frame.height();
int left = frame.left;
int top = frame.top;
int right = frame.right;
int bottom = frame.bottom;
canvas.drawColor(maskColor);
fillRectRound(left, top, right, bottom, 30, 30, false);
canvas.drawPath(path, pen);
canvas.drawPath(path, eraser);
if (maskType == MASK_TYPE_ID_CARD_FRONT) {
locatorDrawable.setBounds(
(int) (left + 601f / 1006 * width),
(int) (top + (110f / 632) * height),
(int) (left + (963f / 1006) * width),
(int) (top + (476f / 632) * height));
} else if (maskType == MASK_TYPE_ID_CARD_BACK) {
locatorDrawable.setBounds(
(int) (left + 51f / 1006 * width),
(int) (top + (48f / 632) * height),
(int) (left + (250f / 1006) * width),
(int) (top + (262f / 632) * height));
} else if (maskType == MASK_TYPE_PASSPORT) {
locatorDrawable.setBounds(
(int) (left + 30f / 1006 * width),
(int) (top + (20f / 632) * height),
(int) (left + (303f / 1006) * width),
(int) (top + (416f / 632) * height));
}
if (locatorDrawable != null) {
locatorDrawable.draw(canvas);
}
}
private Path path = new Path();
private Path fillRectRound(float left, float top, float right, float bottom, float rx, float ry, boolean
conformToOriginalPost) {
path.reset();
if (rx < 0) {
rx = 0;
}
if (ry < 0) {
ry = 0;
}
float width = right - left;
float height = bottom - top;
if (rx > width / 2) {
rx = width / 2;
}
if (ry > height / 2) {
ry = height / 2;
}
float widthMinusCorners = (width - (2 * rx));
float heightMinusCorners = (height - (2 * ry));
path.moveTo(right, top + ry);
path.rQuadTo(0, -ry, -rx, -ry);
path.rLineTo(-widthMinusCorners, 0);
path.rQuadTo(-rx, 0, -rx, ry);
path.rLineTo(0, heightMinusCorners);
if (conformToOriginalPost) {
path.rLineTo(0, ry);
path.rLineTo(width, 0);
path.rLineTo(0, -ry);
} else {
path.rQuadTo(0, ry, rx, ry);
path.rLineTo(widthMinusCorners, 0);
path.rQuadTo(rx, 0, rx, -ry);
}
path.rLineTo(0, -heightMinusCorners);
path.close();
return path;
}
{
// 硬件加速不支持,图层混合。
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
pen.setColor(Color.WHITE);
pen.setStyle(Paint.Style.STROKE);
pen.setStrokeWidth(6);
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
private void capture(File file) {
}
}
@@ -0,0 +1,166 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import com.baidu.ocr.ui.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
public class OCRCameraLayout extends FrameLayout {
public static int ORIENTATION_PORTRAIT = 0;
public static int ORIENTATION_HORIZONTAL = 1;
private int orientation = ORIENTATION_PORTRAIT;
private View contentView;
private View centerView;
private View leftDownView;
private View rightUpView;
private int contentViewId;
private int centerViewId;
private int leftDownViewId;
private int rightUpViewId;
public void setOrientation(int orientation) {
if (this.orientation == orientation) {
return;
}
this.orientation = orientation;
requestLayout();
}
public OCRCameraLayout(Context context) {
super(context);
}
public OCRCameraLayout(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttrs(attrs);
}
public OCRCameraLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
parseAttrs(attrs);
}
{
setWillNotDraw(false);
}
private void parseAttrs(AttributeSet attrs) {
TypedArray a = getContext().getTheme().obtainStyledAttributes(
attrs,
R.styleable.OCRCameraLayout,
0, 0);
try {
contentViewId = a.getResourceId(R.styleable.OCRCameraLayout_contentView, -1);
centerViewId = a.getResourceId(R.styleable.OCRCameraLayout_centerView, -1);
leftDownViewId = a.getResourceId(R.styleable.OCRCameraLayout_leftDownView, -1);
rightUpViewId = a.getResourceId(R.styleable.OCRCameraLayout_rightUpView, -1);
} finally {
a.recycle();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
contentView = findViewById(contentViewId);
if (centerViewId != -1) {
centerView = findViewById(centerViewId);
}
leftDownView = findViewById(leftDownViewId);
rightUpView = findViewById(rightUpViewId);
}
private Rect backgroundRect = new Rect();
private Paint paint = new Paint();
{
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.argb(83, 0, 0, 0));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = getWidth();
int height = getHeight();
int left;
int top;
ViewGroup.MarginLayoutParams leftDownViewLayoutParams = (MarginLayoutParams) leftDownView.getLayoutParams();
ViewGroup.MarginLayoutParams rightUpViewLayoutParams = (MarginLayoutParams) rightUpView.getLayoutParams();
if (r < b) {
int contentHeight = width * 4 / 3;
int heightLeft = height - contentHeight;
contentView.layout(l, t, r, contentHeight);
backgroundRect.left = 0;
backgroundRect.top = contentHeight;
backgroundRect.right = width;
backgroundRect.bottom = height;
// layout centerView;
if (centerView != null) {
left = (width - centerView.getMeasuredWidth()) / 2;
top = contentHeight + (heightLeft - centerView.getMeasuredHeight()) / 2;
centerView
.layout(left, top, left + centerView.getMeasuredWidth(), top + centerView.getMeasuredHeight());
}
// layout leftDownView
left = leftDownViewLayoutParams.leftMargin;
top = contentHeight + (heightLeft - leftDownView.getMeasuredHeight()) / 2;
leftDownView
.layout(left, top, left + leftDownView.getMeasuredWidth(), top + leftDownView.getMeasuredHeight());
// layout rightUpView
left = width - rightUpView.getMeasuredWidth() - rightUpViewLayoutParams.rightMargin;
top = contentHeight + (heightLeft - rightUpView.getMeasuredHeight()) / 2;
rightUpView.layout(left, top, left + rightUpView.getMeasuredWidth(), top + rightUpView.getMeasuredHeight());
} else {
int contentWidth = height * 4 / 3;
int widthLeft = width - contentWidth;
contentView.layout(l, t, contentWidth, height);
backgroundRect.left = contentWidth;
backgroundRect.top = 0;
backgroundRect.right = width;
backgroundRect.bottom = height;
// layout centerView
if (centerView != null) {
left = contentWidth + (widthLeft - centerView.getMeasuredWidth()) / 2;
top = (height - centerView.getMeasuredHeight()) / 2;
centerView
.layout(left, top, left + centerView.getMeasuredWidth(), top + centerView.getMeasuredHeight());
}
// layout leftDownView
left = contentWidth + (widthLeft - leftDownView.getMeasuredWidth()) / 2;
top = height - leftDownView.getMeasuredHeight() - leftDownViewLayoutParams.bottomMargin;
leftDownView
.layout(left, top, left + leftDownView.getMeasuredWidth(), top + leftDownView.getMeasuredHeight());
// layout rightUpView
left = contentWidth + (widthLeft - rightUpView.getMeasuredWidth()) / 2;
top = rightUpViewLayoutParams.topMargin;
rightUpView.layout(left, top, left + rightUpView.getMeasuredWidth(), top + rightUpView.getMeasuredHeight());
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(backgroundRect, paint);
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class OCRFrameLayout extends ViewGroup {
public OCRFrameLayout(Context context) {
super(context);
}
public OCRFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttrs(attrs);
}
public OCRFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
parseAttrs(attrs);
}
private void parseAttrs(AttributeSet attrs) {
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
view.layout(l, t, r, b);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width;
int height;
int childCount = getChildCount();
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
measureChild(view, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec
(height, MeasureSpec.EXACTLY));
}
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
}
}
@@ -0,0 +1,8 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.camera;
public interface PermissionCallback {
boolean onRequestPermission();
}
@@ -0,0 +1,325 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.crop;
import java.io.IOException;
import com.baidu.ocr.ui.util.ImageUtil;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.ExifInterface;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.WindowManager;
public class CropView extends View {
public CropView(Context context) {
super(context);
init();
}
public CropView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CropView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setFilePath(String path) {
if (this.bitmap != null && !this.bitmap.isRecycled()) {
this.bitmap.recycle();
}
if (path == null) {
return;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap original = BitmapFactory.decodeFile(path, options);
try {
ExifInterface exif = new ExifInterface(path);
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Matrix matrix = new Matrix();
int rotationInDegrees = ImageUtil.exifToDegrees(rotation);
if (rotation != 0f) {
matrix.preRotate(rotationInDegrees);
}
// 图片太大会导致内存泄露,所以在显示前对图片进行裁剪。
int maxPreviewImageSize = 2560;
int min = Math.min(options.outWidth, options.outHeight);
min = Math.min(min, maxPreviewImageSize);
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Point screenSize = new Point();
windowManager.getDefaultDisplay().getSize(screenSize);
min = Math.min(min, screenSize.x * 2 / 3);
options.inSampleSize = ImageUtil.calculateInSampleSize(options, min, min);
options.inScaled = true;
options.inDensity = options.outWidth;
options.inTargetDensity = min * options.inSampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = false;
this.bitmap = BitmapFactory.decodeFile(path, options);
} catch (IOException e) {
e.printStackTrace();
this.bitmap = original;
} catch (NullPointerException e) {
e.printStackTrace();
}
setBitmap(this.bitmap);
}
private void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
matrix.reset();
centerImage(getWidth(), getHeight());
rotation = 0;
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerImage(w, h);
invalidate();
}
public Bitmap crop(Rect frame) {
float scale = getScale();
float[] src = new float[] {frame.left, frame.top};
float[] desc = new float[] {0, 0};
Matrix invertedMatrix = new Matrix();
this.matrix.invert(invertedMatrix);
invertedMatrix.mapPoints(desc, src);
Matrix matrix = new Matrix();
int width = (int) (frame.width() / scale);
int height = (int) (frame.height() / scale);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
Bitmap originalBitmap = this.bitmap;
matrix.postTranslate(-desc[0], -desc[1]);
canvas.drawBitmap(originalBitmap, matrix, null);
return bitmap;
}
public void setMinimumScale(float setMinimumScale) {
this.setMinimumScale = setMinimumScale;
}
public void setMaximumScale(float maximumScale) {
this.maximumScale = maximumScale;
}
private float setMinimumScale = 0.2f;
private float maximumScale = 4.0f;
private float[] matrixArray = new float[9];
private Matrix matrix = new Matrix();
private Bitmap bitmap;
private GestureDetector gestureDetector;
private ScaleGestureDetector scaleGestureDetector;
private ScaleGestureDetector.OnScaleGestureListener onScaleGestureListener =
new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scale(detector);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
matrix.postScale(scale, scale);
invalidate();
}
};
private void init() {
scaleGestureDetector = new ScaleGestureDetector(getContext(), onScaleGestureListener);
gestureDetector = new GestureDetector(getContext(), new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
translate(distanceX, distanceY);
return true;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});
}
int rotation = 0;
public void rotate(int degrees) {
if (this.bitmap == null) {
return;
}
Matrix matrix = new Matrix();
int dx = this.bitmap.getWidth() / 2;
int dy = this.bitmap.getHeight() / 2;
matrix.postTranslate(-dx, -dy);
matrix.postRotate(degrees);
matrix.postTranslate(dy, dx);
Bitmap scaledBitmap = this.bitmap;
Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap.getHeight(), scaledBitmap.getWidth(),
Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(rotatedBitmap);
canvas.drawBitmap(this.bitmap, matrix, null);
this.bitmap.recycle();
this.bitmap = rotatedBitmap;
centerImage(getWidth(), getHeight());
invalidate();
}
private void translate(float distanceX, float distanceY) {
matrix.getValues(matrixArray);
float left = matrixArray[Matrix.MTRANS_X];
float top = matrixArray[Matrix.MTRANS_Y];
Rect bound = getRestrictedBound();
if (bound != null) {
float scale = getScale();
float right = left + (int) (bitmap.getWidth() / scale);
float bottom = top + (int) (bitmap.getHeight() / scale);
if (left - distanceX > bound.left) {
distanceX = left - bound.left;
}
if (top - distanceY > bound.top) {
distanceY = top - bound.top;
}
if (distanceX > 0) {
if (right - distanceX < bound.right) {
distanceX = right - bound.right;
}
}
if (distanceY > 0) {
if (bottom - distanceY < bound.bottom) {
distanceY = bottom - bound.bottom;
}
}
}
matrix.postTranslate(-distanceX, -distanceY);
invalidate();
}
private void scale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
float currentScale = getScale();
if (currentScale * scale < setMinimumScale) {
scale = setMinimumScale / currentScale;
}
if (currentScale * scale > maximumScale) {
scale = maximumScale / currentScale;
}
matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
invalidate();
}
private void centerImage(int width, int height) {
if (width <= 0 || height <= 0 || bitmap == null) {
return;
}
float widthRatio = 1.0f * height / this.bitmap.getHeight();
float heightRatio = 1.0f * width / this.bitmap.getWidth();
float ratio = Math.min(widthRatio, heightRatio);
float dx = (width - this.bitmap.getWidth()) / 2;
float dy = (height - this.bitmap.getHeight()) / 2;
matrix.setTranslate(0, 0);
matrix.setScale(ratio, ratio, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
matrix.postTranslate(dx, dy);
invalidate();
}
private float getScale() {
matrix.getValues(matrixArray);
float scale = matrixArray[Matrix.MSCALE_X];
if (Math.abs(scale) <= 0.1) {
scale = matrixArray[Matrix.MSKEW_X];
}
return Math.abs(scale);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap != null) {
canvas.drawBitmap(bitmap, matrix, null);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = scaleGestureDetector.onTouchEvent(event);
result = gestureDetector.onTouchEvent(event) || result;
return result || super.onTouchEvent(event);
}
private Rect restrictBound;
private Rect getRestrictedBound() {
return restrictBound;
}
public void setRestrictBound(Rect rect) {
this.restrictBound = rect;
}
}
@@ -0,0 +1,290 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.crop;
import com.baidu.ocr.ui.util.DimensionUtil;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class FrameOverlayView extends View {
interface OnFrameChangeListener {
void onFrameChange(RectF newFrame);
}
public Rect getFrameRect() {
Rect rect = new Rect();
rect.left = (int) frameRect.left;
rect.top = (int) frameRect.top;
rect.right = (int) frameRect.right;
rect.bottom = (int) frameRect.bottom;
return rect;
}
public FrameOverlayView(Context context) {
super(context);
init();
}
public FrameOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FrameOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
translate(distanceX, distanceY);
return true;
}
};
private static final int CORNER_LEFT_TOP = 1;
private static final int CORNER_RIGHT_TOP = 2;
private static final int CORNER_RIGHT_BOTTOM = 3;
private static final int CORNER_LEFT_BOTTOM = 4;
private int currentCorner = -1;
int margin = 20;
int cornerLength = 100;
int cornerLineWidth = 6;
private int maskColor = Color.argb(180, 0, 0, 0);
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint eraser = new Paint(Paint.ANTI_ALIAS_FLAG);
private GestureDetector gestureDetector;
private RectF touchRect = new RectF();
private RectF frameRect = new RectF();
private OnFrameChangeListener onFrameChangeListener;
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(6);
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void setOnFrameChangeListener(OnFrameChangeListener onFrameChangeListener) {
this.onFrameChangeListener = onFrameChangeListener;
}
private void init() {
gestureDetector = new GestureDetector(getContext(), onGestureListener);
cornerLength = DimensionUtil.dpToPx(18);
cornerLineWidth = DimensionUtil.dpToPx(3);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
resetFrameRect(w, h);
}
private void resetFrameRect(int w, int h) {
if (shapeType == 1) {
frameRect.left = (int) (w * 0.05);
frameRect.top = (int) (h * 0.25);
} else {
frameRect.left = (int) (w * 0.2);
frameRect.top = (int) (h * 0.2);
}
frameRect.right = w - frameRect.left;
frameRect.bottom = h - frameRect.top;
}
private int shapeType = 0;
public void setTypeWide() {
shapeType = 1;
}
private void translate(float x, float y) {
if (x > 0) {
// moving left;
if (frameRect.left - x < margin) {
x = frameRect.left - margin;
}
} else {
if (frameRect.right - x > getWidth() - margin) {
x = frameRect.right - getWidth() + margin;
}
}
if (y > 0) {
if (frameRect.top - y < margin) {
y = frameRect.top - margin;
}
} else {
if (frameRect.bottom - y > getHeight() - margin) {
y = frameRect.bottom - getHeight() + margin;
}
}
frameRect.offset(-x, -y);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(maskColor);
paint.setStrokeWidth(DimensionUtil.dpToPx(1));
canvas.drawRect(frameRect, paint);
canvas.drawRect(frameRect, eraser);
drawCorners(canvas);
}
private void drawCorners(Canvas canvas) {
paint.setStrokeWidth(cornerLineWidth);
// left top
drawLine(canvas, frameRect.left - cornerLineWidth / 2, frameRect.top, cornerLength, 0);
drawLine(canvas, frameRect.left, frameRect.top, 0, cornerLength);
// right top
drawLine(canvas, frameRect.right + cornerLineWidth / 2, frameRect.top, -cornerLength, 0);
drawLine(canvas, frameRect.right, frameRect.top, 0, cornerLength);
// right bottom
drawLine(canvas, frameRect.right, frameRect.bottom, 0, -cornerLength);
drawLine(canvas, frameRect.right + cornerLineWidth / 2, frameRect.bottom, -cornerLength, 0);
// left bottom
drawLine(canvas, frameRect.left - cornerLineWidth / 2, frameRect.bottom, cornerLength, 0);
drawLine(canvas, frameRect.left, frameRect.bottom, 0, -cornerLength);
}
private void drawLine(Canvas canvas, float x, float y, int dx, int dy) {
canvas.drawLine(x, y, x + dx, y + dy, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = handleDown(event);
float ex = 60;
RectF rectExtend = new RectF(frameRect.left - ex, frameRect.top - ex,
frameRect.right + ex, frameRect.bottom + ex);
if (!result) {
if (rectExtend.contains(event.getX(), event.getY())) {
gestureDetector.onTouchEvent(event);
return true;
}
}
return result;
}
private boolean handleDown(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
currentCorner = -1;
break;
case MotionEvent.ACTION_DOWN: {
float radius = cornerLength;
touchRect.set(event.getX() - radius, event.getY() - radius, event.getX() + radius,
event.getY() + radius);
if (touchRect.contains(frameRect.left, frameRect.top)) {
currentCorner = CORNER_LEFT_TOP;
return true;
}
if (touchRect.contains(frameRect.right, frameRect.top)) {
currentCorner = CORNER_RIGHT_TOP;
return true;
}
if (touchRect.contains(frameRect.right, frameRect.bottom)) {
currentCorner = CORNER_RIGHT_BOTTOM;
return true;
}
if (touchRect.contains(frameRect.left, frameRect.bottom)) {
currentCorner = CORNER_LEFT_BOTTOM;
return true;
}
return false;
}
case MotionEvent.ACTION_MOVE:
return handleScale(event);
default:
}
return false;
}
private boolean handleScale(MotionEvent event) {
switch (currentCorner) {
case CORNER_LEFT_TOP:
scaleTo(event.getX(), event.getY(), frameRect.right, frameRect.bottom);
return true;
case CORNER_RIGHT_TOP:
scaleTo(frameRect.left, event.getY(), event.getX(), frameRect.bottom);
return true;
case CORNER_RIGHT_BOTTOM:
scaleTo(frameRect.left, frameRect.top, event.getX(), event.getY());
return true;
case CORNER_LEFT_BOTTOM:
scaleTo(event.getX(), frameRect.top, frameRect.right, event.getY());
return true;
default:
return false;
}
}
private void scaleTo(float left, float top, float right, float bottom) {
if (bottom - top < getMinimumFrameHeight()) {
top = frameRect.top;
bottom = frameRect.bottom;
}
if (right - left < getMinimumFrameWidth()) {
left = frameRect.left;
right = frameRect.right;
}
left = Math.max(margin, left);
top = Math.max(margin, top);
right = Math.min(getWidth() - margin, right);
bottom = Math.min(getHeight() - margin, bottom);
frameRect.set(left, top, right, bottom);
invalidate();
}
private void notifyFrameChange() {
if (onFrameChangeListener != null) {
onFrameChangeListener.onFrameChange(frameRect);
}
}
private float getMinimumFrameWidth() {
return 2.4f * cornerLength;
}
private float getMinimumFrameHeight() {
return 2.4f * cornerLength;
}
}
@@ -0,0 +1,14 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.util;
import android.content.res.Resources;
public class DimensionUtil {
public static int dpToPx(int dp) {
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
}
@@ -0,0 +1,159 @@
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.ocr.ui.util;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.util.Log;
public class ImageUtil {
private static final String TAG = "CameraExif";
public static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}
// Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
public static int getOrientation(byte[] jpeg) {
if (jpeg == null) {
return 0;
}
int offset = 0;
int length = 0;
// ISO/IEC 10918-1:1993(E)
while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
int marker = jpeg[offset] & 0xFF;
// Check if the marker is a padding.
if (marker == 0xFF) {
continue;
}
offset++;
// Check if the marker is SOI or TEM.
if (marker == 0xD8 || marker == 0x01) {
continue;
}
// Check if the marker is EOI or SOS.
if (marker == 0xD9 || marker == 0xDA) {
break;
}
// Get the length and check if it is reasonable.
length = pack(jpeg, offset, 2, false);
if (length < 2 || offset + length > jpeg.length) {
Log.e(TAG, "Invalid length");
return 0;
}
// Break if the marker is EXIF in APP1.
if (marker == 0xE1 && length >= 8
&& pack(jpeg, offset + 2, 4, false) == 0x45786966
&& pack(jpeg, offset + 6, 2, false) == 0) {
offset += 8;
length -= 8;
break;
}
// Skip other markers.
offset += length;
length = 0;
}
// JEITA CP-3451 Exif Version 2.2
if (length > 8) {
// Identify the byte order.
int tag = pack(jpeg, offset, 4, false);
if (tag != 0x49492A00 && tag != 0x4D4D002A) {
Log.e(TAG, "Invalid byte order");
return 0;
}
boolean littleEndian = (tag == 0x49492A00);
// Get the offset and check if it is reasonable.
int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
if (count < 10 || count > length) {
Log.e(TAG, "Invalid offset");
return 0;
}
offset += count;
length -= count;
// Get the count and go through all the elements.
count = pack(jpeg, offset - 2, 2, littleEndian);
while (count-- > 0 && length >= 12) {
// Get the tag and check if it is orientation.
tag = pack(jpeg, offset, 2, littleEndian);
if (tag == 0x0112) {
// We do not really care about type and count, do we?
int orientation = pack(jpeg, offset + 8, 2, littleEndian);
switch (orientation) {
case 1:
return 0;
case 3:
return 180;
case 6:
return 90;
case 8:
return 270;
default:
return 0;
}
}
offset += 12;
length -= 12;
}
}
Log.i(TAG, "Orientation not found");
return 0;
}
private static int pack(byte[] bytes, int offset, int length,
boolean littleEndian) {
int step = 1;
if (littleEndian) {
offset += length - 1;
step = -1;
}
int value = 0;
while (length-- > 0) {
value = (value << 8) | (bytes[offset] & 0xFF);
offset += step;
}
return value;
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#000000" />
<corners android:radius="12dp" />
</shape>
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按压时 -->
<item android:drawable="@drawable/bd_ocr_take_photo_highlight" android:state_pressed="true" />
<!-- 被选中时 -->
<item android:drawable="@drawable/bd_ocr_take_photo_normal" android:state_selected="true" />
<!-- 被激活时 -->
<item android:drawable="@drawable/bd_ocr_take_photo_normal" android:state_activated="true" />
<!-- 默认时 -->
<item android:drawable="@drawable/bd_ocr_take_photo_normal" />
</selector>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/take_picture_container"
android:layout_height="match_parent"
android:layout_width="match_parent"
layout="@layout/bd_ocr_take_picture"/>
<include
android:id="@+id/crop_container"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:visibility="gone"
layout="@layout/bd_ocr_crop"/>
<include
android:id="@+id/confirm_result_container"
layout="@layout/bd_ocr_confirm_result"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<com.baidu.ocr.ui.camera.OCRCameraLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentView="@+id/display_image_view"
app:leftDownView="@+id/cancel_button"
app:rightUpView="@+id/confirm_button">
<ImageView
android:id="@+id/display_image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:srcCompat="@drawable/bd_ocr_id_card_locator_front"/>
<ImageView
android:id="@+id/cancel_button"
android:layout_width="60dp"
android:layout_height="40dp"
android:padding="12dp"
android:src="@drawable/bd_ocr_cancel"
android:layout_marginLeft="@dimen/camera_left_margin"
android:layout_marginStart="@dimen/camera_left_margin"
android:layout_marginBottom="@dimen/camera_bottom_margin"
/>
<ImageView
android:id="@+id/confirm_button"
android:layout_width="60dp"
android:layout_height="40dp"
android:padding="12dp"
android:src="@drawable/bd_ocr_confirm"
android:layout_marginRight="@dimen/camera_right_margin"
android:layout_marginEnd="@dimen/camera_right_margin"
android:layout_marginTop="@dimen/camera_top_margin"
/>
</com.baidu.ocr.ui.camera.OCRCameraLayout>
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<com.baidu.ocr.ui.camera.OCRCameraLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentView="@+id/crop_view_container"
app:leftDownView="@+id/cancel_button"
app:centerView="@+id/rotate_button"
app:rightUpView="@+id/confirm_button">
<com.baidu.ocr.ui.camera.OCRFrameLayout
android:id="@+id/crop_view_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.baidu.ocr.ui.crop.CropView
android:id="@+id/crop_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.baidu.ocr.ui.crop.FrameOverlayView
android:id="@+id/overlay_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.baidu.ocr.ui.camera.MaskView
android:id="@+id/crop_mask_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.baidu.ocr.ui.camera.OCRFrameLayout>
<ImageView
android:id="@+id/rotate_button"
android:layout_width="60dp"
android:layout_height="58dp"
android:padding="18dp"
android:src="@drawable/bd_ocr_rotate"/>
<ImageView
android:id="@+id/confirm_button"
android:layout_width="60dp"
android:layout_height="40dp"
android:padding="12dp"
android:src="@drawable/bd_ocr_confirm"
android:layout_marginRight="@dimen/camera_right_margin"
android:layout_marginEnd="@dimen/camera_right_margin"
android:layout_marginTop="@dimen/camera_top_margin"/>
<ImageView
android:id="@+id/cancel_button"
android:layout_width="60dp"
android:layout_height="40dp"
android:padding="12dp"
android:src="@drawable/bd_ocr_cancel"
android:layout_marginLeft="@dimen/camera_left_margin"
android:layout_marginStart="@dimen/camera_left_margin"
android:layout_marginBottom="@dimen/camera_bottom_margin"/>
</com.baidu.ocr.ui.camera.OCRCameraLayout>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<com.baidu.ocr.ui.camera.OCRCameraLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentView="@+id/camera_view"
app:leftDownView="@+id/album_button"
app:centerView="@+id/take_photo_button"
app:rightUpView="@+id/light_button">
<com.baidu.ocr.ui.camera.CameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.baidu.ocr.ui.camera.CameraView>
<ImageView
android:id="@+id/album_button"
android:layout_width="60dp"
android:layout_height="54dp"
android:padding="18dp"
android:layout_marginLeft="@dimen/camera_left_margin"
android:layout_marginStart="@dimen/camera_left_margin"
android:layout_marginBottom="@dimen/camera_bottom_margin"
android:src="@drawable/bd_ocr_gallery"/>
<ImageView
android:id="@+id/take_photo_button"
android:layout_width="58dp"
android:layout_height="58dp"
android:clickable="true"
android:background="@drawable/bd_ocr_take_photo_selector"/>
<ImageView
android:id="@+id/light_button"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="15dp"
android:clickable="true"
android:layout_marginRight="@dimen/camera_right_margin"
android:layout_marginEnd="@dimen/camera_right_margin"
android:layout_marginTop="@dimen/camera_top_margin"
android:paddingBottom="3dp"
android:src="@drawable/bd_ocr_light_off"
/>
</com.baidu.ocr.ui.camera.OCRCameraLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<resources>
<dimen name="camera_left_margin">18dp</dimen>
<dimen name="camera_right_margin">18dp</dimen>
<dimen name="camera_bottom_margin">16dp</dimen>
<dimen name="camera_top_margin">16dp</dimen>
</resources>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<resources>
<declare-styleable name="OCRCameraLayout">
<attr name="contentView" format="reference"/>
<attr name="centerView" format="reference"/>
<attr name="leftDownView" format="reference"/>
<attr name="rightUpView" format="reference"/>
</declare-styleable>
</resources>
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
+7
View File
@@ -0,0 +1,7 @@
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<resources>
<string name="app_name">OCR_UI</string>
<string name="camera_permission_required">本功能需要相机权限!</string>
</resources>
+14
View File
@@ -0,0 +1,14 @@
<!--
~ Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
-->
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
+2
View File
@@ -6,3 +6,5 @@ include ':record'
//include ':ocr_ui' //include ':ocr_ui'
//include ':AliyunListPlayer' //include ':AliyunListPlayer'
include ':mylibrary' include ':mylibrary'
include ':ocr_ui'
include ':ocr_ui'