Android使用百度地图定位并显示手机位置后使用前置摄像头“偷拍”

今天老板让我验证一下技术可行性,记录下来。

需求 :定位手机的位置并在百度地图上显示,得到位置后使用前置摄像头进行抓拍


拿到这个需求后,对于摄像头的使用不太熟悉,于是我先做了定位手机并在百度地图上显示的功能


访问了百度地图api官网http://lbsyun.baidu.com/找到Android地图以及定位使用部分,官网上有详尽的使用指南,这里只简单总结描述一下,首先复制粘贴jar包和so文件

如图,jar包文件最好与so文件版本一致

 

  1 package com.agile.androiddgs.activity;
  2 
  3 import android.Manifest;
  4 import android.annotation.TargetApi;
  5 import android.app.Activity;
  6 import android.content.pm.PackageManager;
  7 import android.graphics.Bitmap;
  8 import android.graphics.BitmapFactory;
  9 import android.hardware.Camera;
 10 import android.media.MediaScannerConnection;
 11 import android.net.Uri;
 12 import android.os.Build;
 13 import android.os.Bundle;
 14 import android.os.Handler;
 15 import android.util.Log;
 16 import android.view.Surface;
 17 import android.view.SurfaceHolder;
 18 import android.view.SurfaceView;
 19 import android.view.Window;
 20 
 21 import com.agile.androiddgs.R;
 22 import com.baidu.location.BDLocation;
 23 import com.baidu.location.BDLocationListener;
 24 import com.baidu.location.LocationClient;
 25 import com.baidu.location.LocationClientOption;
 26 import com.baidu.mapapi.SDKInitializer;
 27 import com.baidu.mapapi.map.BaiduMap;
 28 import com.baidu.mapapi.map.BitmapDescriptor;
 29 import com.baidu.mapapi.map.MapStatusUpdate;
 30 import com.baidu.mapapi.map.MapStatusUpdateFactory;
 31 import com.baidu.mapapi.map.MapView;
 32 import com.baidu.mapapi.map.MyLocationData;
 33 import com.baidu.mapapi.model.LatLng;
 34 import com.baidu.mapapi.search.core.SearchResult;
 35 import com.baidu.mapapi.search.geocode.GeoCodeResult;
 36 import com.baidu.mapapi.search.geocode.GeoCoder;
 37 import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
 38 import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
 39 import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;
 40 
 41 import java.io.ByteArrayInputStream;
 42 import java.io.ByteArrayOutputStream;
 43 import java.util.ArrayList;
 44 import java.util.List;
 45 
 46 public class PositionActivity extends Activity implements OnGetGeoCoderResultListener{
 47     /**********************百度地图定位以及地图功能*********************************/
 48     private String permissionInfo;
 49     private final int SDK_PERMISSION_REQUEST = 127;
 50     private MapView mapView = null;
 51     private BaiduMap baiduMap = null;
 52 
 53     // 定位相关声明
 54     private LocationClient locationClient = null;
 55     //自定义图标
 56     private BitmapDescriptor mCurrentMarker = null;
 57     private boolean isFirstLoc = true;// 是否首次定位
 58 
 59     GeoCoder mSearch = null; // 搜索模块,也可去掉地图模块独立使用
 60 
 61     private BDLocationListener myListener = new BDLocationListener() {
 62         @Override
 63         public void onReceiveLocation(BDLocation location) {//定位成功
 64             // map view 销毁后不在处理新接收的位置
 65             if (location == null || mapView == null)
 66                 return;
 67             try {
 68                mSearch.reverseGeoCode(new ReverseGeoCodeOption().location(new LatLng(location.getLatitude(), location.getLongitude())));
 69 
 70             }catch (Exception e){
 71                 e.printStackTrace();
 72             }
 73 
 74             MyLocationData locData = new MyLocationData.Builder()
 75                     .accuracy(location.getRadius())
 76                     // 此处设置开发者获取到的方向信息,顺时针0-360
 77                     .direction(100).latitude(location.getLatitude())
 78                     .longitude(location.getLongitude()).build();
 79             baiduMap.setMyLocationData(locData);    //设置定位数据
 80 
 81 
 82             if (isFirstLoc) {//第一次定位
 83                 isFirstLoc = false;
 84 
 85 
 86                 LatLng ll = new LatLng(location.getLatitude(),
 87                         location.getLongitude());
 88                 MapStatusUpdate u = MapStatusUpdateFactory.newLatLngZoom(ll, 16);    //设置地图中心点以及缩放级别
 89 //                MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(ll);
 90                 baiduMap.animateMapStatus(u);
 91             }
 92         }
 93     };
 94 
 95     /**********************************摄像头***********************************************/
 96     private SurfaceView mySurfaceView;
 97     private SurfaceHolder myHolder;
 98     private Camera myCamera;
 99     int mCurrentCamIndex = 0;
100 
101     @Override
102     protected void onCreate(Bundle savedInstanceState) {
103         super.onCreate(savedInstanceState);
104         // after andrioid m,must request Permiision on runtime
105         getPersimmions();
106         requestWindowFeature(Window.FEATURE_NO_TITLE);
107         // 在使用SDK各组件之前初始化context信息,传入ApplicationContext
108         // 注意该方法要再setContentView方法之前实现
109         SDKInitializer.initialize(getApplicationContext());
110         setContentView(R.layout.activity_position);
111 
112         // 初始化搜索模块,注册事件监听
113         mSearch = GeoCoder.newInstance();
114 
115         mapView = (MapView) this.findViewById(R.id.mapView); // 获取地图控件引用
116         baiduMap = mapView.getMap();
117         //开启定位图层
118         baiduMap.setMyLocationEnabled(true);
119 
120         locationClient = new LocationClient(getApplicationContext()); // 实例化LocationClient类
121         locationClient.registerLocationListener(myListener); // 注册监听函数
122         this.setLocationOption();    //设置定位参数
123         locationClient.start(); // 开始定位
124         // baiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL); // 设置为一般地图
125 
126         // baiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE); //设置为卫星地图
127         // baiduMap.setTrafficEnabled(true); //开启交通图
128       
129     }
130 
131     @TargetApi(23)
132     private void getPersimmions() {
133         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
134             ArrayList<String> permissions = new ArrayList<String>();
135             /***
136              * 定位权限为必须权限,用户如果禁止,则每次进入都会申请
137              */
138             // 定位精确位置
139             if(checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
140                 permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
141             }
142             if(checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
143                 permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
144             }
145             /*
146              * 读写权限和电话状态权限非必要权限(建议授予)只会申请一次,用户同意或者禁止,只会弹一次
147              */
148             // 读写权限
149             if (addPermission(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
150                 permissionInfo += "Manifest.permission.WRITE_EXTERNAL_STORAGE Deny \n";
151             }
152             // 读取电话状态权限
153             if (addPermission(permissions, Manifest.permission.READ_PHONE_STATE)) {
154                 permissionInfo += "Manifest.permission.READ_PHONE_STATE Deny \n";
155             }
156 
157             if (permissions.size() > 0) {
158                 requestPermissions(permissions.toArray(new String[permissions.size()]), SDK_PERMISSION_REQUEST);
159             }
160         }
161     }
162 
163     @TargetApi(23)
164     private boolean addPermission(ArrayList<String> permissionsList, String permission) {
165         if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { // 如果应用没有获得对应权限,则添加到列表中,准备批量申请
166             if (shouldShowRequestPermissionRationale(permission)){
167                 return true;
168             }else{
169                 permissionsList.add(permission);
170                 return false;
171             }
172 
173         }else{
174             return true;
175         }
176     }
177 
178     @TargetApi(23)
179     @Override
180     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
181         // TODO Auto-generated method stub
182         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
183 
184     }
185 
186     // 三个状态实现地图生命周期管理
187     @Override
188     protected void onDestroy() {
189         //退出时销毁定位
190         locationClient.stop();
191         baiduMap.setMyLocationEnabled(false);
192         // TODO Auto-generated method stub
193         super.onDestroy();
194         mapView.onDestroy();
195         mapView = null;
196     }
197 
198     @Override
199     protected void onResume() {
200         // TODO Auto-generated method stub
201         super.onResume();
202         mapView.onResume();
203     }
204 
205     @Override
206     protected void onPause() {
207         // TODO Auto-generated method stub
208         super.onPause();
209         mapView.onPause();
210     }
211 
212 
213 
214     /**
215      * 设置定位参数
216      */
217     private void setLocationOption() {
218         LocationClientOption option = new LocationClientOption();
219         option.setOpenGps(true); // 打开GPS
220         option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);// 设置定位模式
221         option.setCoorType("bd09ll"); // 返回的定位结果是百度经纬度,默认值gcj02
222         option.setScanSpan(5000); // 设置发起定位请求的间隔时间为5000ms
223         option.setIsNeedAddress(true); // 返回的定位结果包含地址信息
224         option.setNeedDeviceDirect(true); // 返回的定位结果包含手机机头的方向
225 
226         locationClient.setLocOption(option);
227     }
228 
229     
230     @Override
231     public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
232 
233     }
234     /**
235     根据经纬度反编为具体地址
236 */
237     @Override
238     public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
239         if (reverseGeoCodeResult == null || reverseGeoCodeResult.error != SearchResult.ERRORNO.NO_ERROR)
240         {
241             return;
242         }
243 
244         String address =  reverseGeoCodeResult.getAddress();
245     }
246 
247 
248 }

上面是定位以及百度地图的使用,下面是摄像头的使用,以及图片压缩(本文使用质量压缩)

 1 //初始化surfaceview
 2         new Thread(new Runnable() {
 3             @Override
 4             public void run() {
 5 
 6                 mySurfaceView = (SurfaceView) findViewById(R.id.camera_surfaceview);
 7 
 8                 //初始化surfaceholder
 9                 myHolder = mySurfaceView.getHolder();
10                 myHolder.addCallback(new SurfaceViewCallback());
11                 myHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
12 
13             }
14         }).start();

在onCreate()方法中另外开启一个线程,用来偷偷的拍照,初始化SurfaceView并为SurfaceView设置callBack方法

  1 /***************************************************************************************/
  2     private final class SurfaceViewCallback implements android.view.SurfaceHolder.Callback {
  3         public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
  4         {
  5 
  6             try {
  7                 myCamera.setPreviewDisplay(arg0);
  8                 myCamera.startPreview();
  9                 setCameraDisplayOrientation(PositionActivity.this, mCurrentCamIndex, myCamera);
 10                 new Handler().postDelayed(new Runnable(){
 11 
 12                     public void run() {
 13               //拍照
 14                         myCamera.takePicture(shutterCallback, rawPictureCallback,
 15                                 jpegPictureCallback);
 16                     }
 17 
 18                 }, 5000);
 19 
 20             } catch (Exception e) {
 21                 e.printStackTrace();
 22             }
 23         }
 24         public void surfaceCreated(SurfaceHolder holder) {
 25 //                mCamera = Camera.open();
 26             //change to front camera
 27             myCamera = openFrontFacingCameraGingerbread();
 28             // get Camera parameters
 29             Camera.Parameters params = myCamera.getParameters();
 30 
 31             List<String> focusModes = params.getSupportedFocusModes();
 32             if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
 33                 // Autofocus mode is supported
 34             }
 35         }
 36 
 37         public void surfaceDestroyed(SurfaceHolder holder) {
 38             myCamera.stopPreview();
 39             myCamera.release();
 40             myCamera = null;
 41         }
 42     }
 43 
 44     //根据横竖屏自动调节preview方向,Starting from API level 14, this method can be called when preview is active.
 45     private static void setCameraDisplayOrientation(Activity activity,int cameraId, Camera camera)
 46     {
 47         Camera.CameraInfo info = new Camera.CameraInfo();
 48         Camera.getCameraInfo(cameraId, info);
 49         int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
 50 
 51         //degrees  the angle that the picture will be rotated clockwise. Valid values are 0, 90, 180, and 270.
 52         //The starting position is 0 (landscape).
 53         int degrees = 0;
 54         switch (rotation)
 55         {
 56             case Surface.ROTATION_0: degrees = 0; break;
 57             case Surface.ROTATION_90: degrees = 90; break;
 58             case Surface.ROTATION_180: degrees = 180; break;
 59             case Surface.ROTATION_270: degrees = 270; break;
 60         }
 61         int result;
 62         if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
 63         {
 64             result = (info.orientation + degrees) % 360;
 65             result = (360 - result) % 360;  // compensate the mirror
 66         }
 67         else
 68         {
 69             // back-facing
 70             result = (info.orientation - degrees + 360) % 360;
 71         }
 72         camera.setDisplayOrientation(result);
 73     }
 74 
 75     public void scanFileToPhotoAlbum(String path) {
 76 
 77         MediaScannerConnection.scanFile(PositionActivity.this,
 78                 new String[] { path }, null,
 79                 new MediaScannerConnection.OnScanCompletedListener() {
 80 
 81                     public void onScanCompleted(String path, Uri uri) {
 82                         Log.i("TAG", "Finished scanning " + path);
 83                     }
 84                 });
 85     }
 86 
 87     private Camera openFrontFacingCameraGingerbread() {
 88         int cameraCount = 0;
 89         Camera cam = null;
 90         Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
 91         cameraCount = Camera.getNumberOfCameras();
 92 
 93         for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
 94             Camera.getCameraInfo(camIdx, cameraInfo);
 95             if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
 96                 try {
 97                     cam = Camera.open(camIdx);
 98                     mCurrentCamIndex = camIdx;
 99                 } catch (RuntimeException e) {
100                     e.printStackTrace();
101                 }
102             }
103         }
104 
105         return cam;
106     }
107 
108     Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
109         @Override
110         public void onShutter() {
111         }
112     };
113 
114     Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() {
115         @Override
116         public void onPictureTaken(byte[] arg0, Camera arg1) {
117 
118         }
119     };
120 
121     Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() {
122         @Override
123         public void onPictureTaken(byte[] arg0, Camera arg1) {
124             ByteArrayOutputStream baos = new ByteArrayOutputStream();
125 
126             int options = 100;
127             ByteArrayInputStream isBm = new ByteArrayInputStream(arg0);//把压缩后的数据baos存放到ByteArrayInputStream中
128             Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStr
129 
130             bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
131 
132 
133             while ( baos.toByteArray().length / 1024>1) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩
134                 baos.reset();//重置baos即清空baos
135                 bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
136                 options -= 10;//每次都减少10
137             }
138 
139             /*String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
140                     .toString()
141                     + File.separator
142                     + "PicTest_" + System.currentTimeMillis() + ".jpg";
143             File file = new File(fileName);
144             if (!file.getParentFile().exists()) {
145                 file.getParentFile().mkdir();
146             }
147 
148             try {
149                 BufferedOutputStream bos = new BufferedOutputStream(
150                         new FileOutputStream(file));
151                 bos.write(arg0);
152                 bos.flush();
153                 bos.close();
154                 scanFileToPhotoAlbum(file.getAbsolutePath());
155                 Toast.makeText(PositionActivity.this, "[Test] Photo take and store in" + file.toString(), Toast.LENGTH_LONG).show();
156             } catch (Exception e) {
157                 Toast.makeText(PositionActivity.this, "Picture Failed" + e.toString(),
158                         Toast.LENGTH_LONG).show();
159                 e.printStackTrace();
160             }*/
161         };
162     };

布局文件如下

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="fill_parent"
 4     android:layout_height="fill_parent"
 5     android:orientation="vertical" >
 6     <LinearLayout
 7         android:layout_width="fill_parent"
 8         android:layout_height="fill_parent"
 9         android:orientation="vertical" >
10         <!-- 添加地图控件 -->
11         <com.baidu.mapapi.map.MapView
12             android:id="@+id/mapView"
13             android:layout_width="fill_parent"
14             android:layout_height="fill_parent"
15             android:clickable="true" />
16     </LinearLayout>
17 
18     <!-- 预览框,长宽都为0.1 -->
19     <SurfaceView
20         android:id="@+id/camera_surfaceview"
21         android:layout_width="0.1dp"
22         android:layout_height="0.1dp" >
23     </SurfaceView>
24 </LinearLayout>

在布局文件中地图视图占据了整个屏幕,而摄像头预览图不可见,但是存在着,打开之后会开启一个新的线程用来偷偷使用前置摄像头拍照

 

posted @ 2016-11-07 14:16  马飞标  阅读(1318)  评论(0编辑  收藏  举报