Android浮动窗口
package com.example.floatcamera; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.ImageFormat; import android.graphics.PixelFormat; 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.CaptureRequest; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; import android.util.Log; import android.util.Size; import android.util.SparseIntArray; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.view.View.OnTouchListener; import android.view.WindowManager; import android.widget.RelativeLayout; public class TopWindowService extends Service { public static final String OPERATION = "operation"; public static final int OPERATION_SHOW = 100; public static final int OPERATION_HIDE = 101; private static final int HANDLE_CHECK_ACTIVITY = 200; private boolean isAdded = false; // 是否已增加悬浮窗 private static WindowManager wm; private static WindowManager.LayoutParams params; // private Button btn_floatView; private List<String> homeList; // 桌面应用程序包名列表 private ActivityManager mActivityManager; private static final SparseIntArray ORIENTATION = new SparseIntArray(); private static final String TAG = "Test"; static { ORIENTATION.append(Surface.ROTATION_0, 90); ORIENTATION.append(Surface.ROTATION_90, 0); ORIENTATION.append(Surface.ROTATION_180, 270); ORIENTATION.append(Surface.ROTATION_270, 180); } private String mCameraId; private Size mPreviewSize; private Size mCaptureSize; private HandlerThread mCameraThread; private Handler mCameraHandler; private CameraDevice mCameraDevice; private TextureView mTextureView; private ImageReader mImageReader; private CaptureRequest.Builder mCaptureRequestBuilder; private CaptureRequest mCaptureRequest; private CameraCaptureSession mCameraCaptureSession; private RelativeLayout mFloatLayout; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); // homeList = getHomes(); createFloatView(); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); int operation = intent.getIntExtra(OPERATION, OPERATION_SHOW); switch (operation) { case OPERATION_SHOW: mHandler.removeMessages(HANDLE_CHECK_ACTIVITY); mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY); break; case OPERATION_HIDE: mHandler.removeMessages(HANDLE_CHECK_ACTIVITY); break; } startCameraThread(); mTextureView.setSurfaceTextureListener(mTextureListener); } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case HANDLE_CHECK_ACTIVITY: if (isHome()) { if (!isAdded) { wm.addView(mFloatLayout, params); isAdded = true; } } else { if (isAdded) { wm.removeView(mFloatLayout); isAdded = false; } } mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 1000); break; } } }; /** * 创建悬浮窗 */ private void createFloatView() { LayoutInflater inflater = LayoutInflater.from(getApplication()); // 获取浮动窗口视图所在布局 mFloatLayout = (RelativeLayout) inflater.inflate(R.layout.float_view, null); // btn_floatView = new Button(getApplicationContext()); mTextureView = (TextureView) mFloatLayout.findViewById(R.id.textureView); if (mTextureView == null) { Log.d(TAG, "mTextureView == null"); } wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); params = new WindowManager.LayoutParams(); // 设置window type params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; /* * 如果设置为params.type = WindowManager.LayoutParams.TYPE_PHONE; 那么优先级会降低一些, * 即拉下通知栏不可见 */ params.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明 // 设置Window flag params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; /* * 下面的flags属性的效果形同“锁定”。 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。 * wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL | * LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE; */ // 设置悬浮窗的长得宽 params.width = 1000; params.height = 1000; // 设置悬浮窗的Touch监听 mFloatLayout.setOnTouchListener(new OnTouchListener() { int lastX, lastY; int paramX, paramY; public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); paramX = params.x; paramY = params.y; break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; params.x = paramX + dx; params.y = paramY + dy; // 更新悬浮窗位置 wm.updateViewLayout(mFloatLayout, params); break; } return true; } }); wm.addView(mFloatLayout, params); isAdded = true; } private void startCameraThread() { mCameraThread = new HandlerThread("CameraThread"); mCameraThread.start(); mCameraHandler = new Handler(mCameraThread.getLooper()); } /** * 获得属于桌面的应用的应用包名称 * * @return 返回包含所有包名的字符串列表 */ private List<String> getHomes() { List<String> names = new ArrayList<String>(); PackageManager packageManager = this.getPackageManager(); // 属性 Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo ri : resolveInfo) { names.add(ri.activityInfo.packageName); } return names; } /** * 判断当前界面是否是桌面 */ public boolean isHome() { if (mActivityManager == null) { mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); } List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1); return true; // return homeList.contains(rti.get(0).topActivity.getPackageName()); } private TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // 当SurefaceTexture可用的时候,设置相机参数并打开相机 setupCamera(width, height); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; private void setupCamera(int width, int height) { // 获取摄像头的管理者CameraManager CameraManager manager = (CameraManager) 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,它是管理摄像头支持的所有输出格式和尺寸 StreamConfigurationMap map = characteristics .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); assert map != null; // 根据TextureView的尺寸设置预览尺寸 mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height); // 获取相机支持的最大拍照尺寸 mCaptureSize = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth()); } }); // 此ImageReader用于拍照所需 setupImageReader(); mCameraId = cameraId; break; } } catch (CameraAccessException e) { e.printStackTrace(); } } // 选择sizeMap中大于并且最接近width和height的size private Size getOptimalSize(Size[] sizeMap, int width, int height) { List<Size> sizeList = new ArrayList<>(); for (Size option : sizeMap) { if (width > height) { if (option.getWidth() > width && option.getHeight() > height) { sizeList.add(option); } } else { if (option.getWidth() > height && option.getHeight() > width) { sizeList.add(option); } } } if (sizeList.size() > 0) { return Collections.min(sizeList, new Comparator<Size>() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight()); } }); } return sizeMap[0]; } private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { manager.openCamera(mCameraId, mStateCallback, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; startPreview(); } @Override public void onDisconnected(CameraDevice camera) { camera.close(); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { camera.close(); mCameraDevice = null; } }; private void startPreview() { SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture(); mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface previewSurface = new Surface(mSurfaceTexture); try { mCaptureRequestBuilder = mCameraDevice .createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mCaptureRequestBuilder.addTarget(previewSurface); mCameraDevice.createCaptureSession( Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { mCaptureRequest = mCaptureRequestBuilder.build(); mCameraCaptureSession = session; mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private void setupImageReader() { //2代表ImageReader中最多可以获取两帧图像流 mImageReader = ImageReader.newInstance(mCaptureSize.getWidth(), mCaptureSize.getHeight(), ImageFormat.JPEG, 2); mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { mCameraHandler.post(new imageSaver(reader.acquireNextImage())); } }, mCameraHandler); } public static class imageSaver implements Runnable { private Image mImage; public imageSaver(Image image) { mImage = image; } @Override public void run() { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); String path = Environment.getExternalStorageDirectory() + "/DCIM/CameraV2/"; File mImageFile = new File(path); if (!mImageFile.exists()) { mImageFile.mkdir(); } String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String fileName = path + "IMG_" + timeStamp + ".jpg"; FileOutputStream fos = null; try { fos = new FileOutputStream(fileName); fos.write(data, 0, data.length); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.GET_TASKS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera2.full" />
float_view.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextureView android:id="@+id/textureView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_centerVertical="true" /> </RelativeLayout>