android 实现悬浮窗相机后台视频隐秘录制
GitHub上参考了别人做悬浮窗的代码,后面自己加的的相机录像功能
主要功能:
1.悬浮窗录制视频,可实现后台或锁屏使用摄像头录制视频。
2.可自定义悬浮窗大小,可设置成1像素。
3.点击关闭可保存视频到根目录上,文件名为当前时间。
注意:安装后要把应用权限先授权,包括悬浮窗权限,相机和存储卡权限。
MainActivity代码
package dongzhong.testforfloatingwindow;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Intent intent_button,intent_image,intent_video;
private EditText intW,intH;
public static int W = 90;
public static int H = 160;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent_button = new Intent(MainActivity.this, FloatingButtonService.class);
intent_image = new Intent(MainActivity.this, FloatingImageDisplayService.class);
intent_video = new Intent(MainActivity.this, FloatingVideoService.class);
intW = findViewById(R.id.intW);
intH = findViewById(R.id.intH);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
startService(intent_button);
}
} else if (requestCode == 1) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
startService(intent_image);
}
} else if (requestCode == 2) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
startService(intent_video);
}
}
}
public void startFloatingButtonService(View view) {
if (FloatingButtonService.isStarted) {
return;
}
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 0);
} else {
startService(intent_button);
}
}
public void startFloatingImageDisplayService(View view) {
W = Integer.parseInt(intW.getText().toString());
H = Integer.parseInt(intH.getText().toString());
if (FloatingImageDisplayService.isStarted) {
return;
}
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 1);
} else {
startService(intent_image);
}
}
public void startFloatingVideoService(View view) {
W = Integer.parseInt(intW.getText().toString());
H = Integer.parseInt(intH.getText().toString());
if (FloatingVideoService.isStarted) {
return;
}
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 2);
} else {
startService(intent_video);
}
}
public void close(View view) {
if (FloatingButtonService.isStarted){
stopService(intent_button);
}
if (FloatingImageDisplayService.isStarted){
stopService(intent_image);
}
if (FloatingVideoService.isStarted){
stopService(intent_video);
}
}
}
配置文件加入权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="dongzhong.testforfloatingwindow">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.FLASHLIGHT" />
<!-- 调用硬件相机权限 -->
<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:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".FloatingButtonService"></service>
<service android:name=".FloatingImageDisplayService"></service>
<service android:name=".FloatingVideoService"></service>
</application>
</manifest>
窗口布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#BC111933"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#E898A6F4"
android:onClick="startFloatingButtonService"
android:text="start floating button" />
<Button
android:layout_width="match_parent"
android:layout_height="78dp"
android:backgroundTint="#5395CA"
android:onClick="startFloatingImageDisplayService"
android:text="start floating image display" />
<Button
android:layout_width="match_parent"
android:layout_height="93dp"
android:backgroundTint="#3573A5"
android:onClick="startFloatingVideoService"
android:text="start floating video player" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="82dp"
android:backgroundTint="#21425C"
android:onClick="close"
android:text="关闭" />
<EditText
android:id="@+id/intW"
android:layout_width="62dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="90" />
<EditText
android:id="@+id/intH"
android:layout_width="62dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="160" />
</LinearLayout>
悬浮窗录像代码
package dongzhong.testforfloatingwindow;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.media.AudioManager;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* Created by admin on 2018/5/30.
*/
public class FloatingVideoService extends Service {
public static boolean isStarted = false;
public static boolean idRecord = false;
private WindowManager windowManager;
private WindowManager.LayoutParams layoutParams;
//private MediaPlayer mediaPlayer;
private View displayView;
MediaRecorder mediaRecorder;
private android.hardware.Camera camera;
SurfaceHolder surfaceHolder;
public File videoFile;
@Override
public void onCreate() {
super.onCreate();
isStarted = true;
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
layoutParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
layoutParams.format = PixelFormat.RGBA_8888;
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.width = MainActivity.W;;
layoutParams.height = MainActivity.H;;
layoutParams.x = 0;
layoutParams.y = 1500;
//mediaPlayer = new MediaPlayer();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
showFloatingWindow();
return super.onStartCommand(intent, flags, startId);
}
//录像结束,重写销毁方法
@Override
public void onDestroy(){
//释放摄像头资源
mediaRecorder.setOnErrorListener(null);
mediaRecorder.setOnInfoListener(null);
mediaRecorder.setPreviewDisplay(null);
try{
mediaRecorder.stop();
} catch (Exception e) {
e.printStackTrace();
}
mediaRecorder.release();
idRecord = false;
camera.stopPreview();
camera.release();
//camera.lock();
windowManager.removeView(displayView);
isStarted =false;
Toast.makeText(this,"in"+videoFile,Toast.LENGTH_SHORT).show();
}
private void showFloatingWindow() {
if (Settings.canDrawOverlays(this)) {
//布局
LayoutInflater layoutInflater = LayoutInflater.from(this);
displayView = layoutInflater.inflate(R.layout.video_display, null);
displayView.setOnTouchListener(new FloatingOnTouchListener());//触摸移动窗口
//mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
SurfaceView surfaceView = displayView.findViewById(R.id.video_display_surfaceview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
//mediaPlayer.setDisplay(surfaceHolder);//网络视频播放
camera = Camera.open();
record();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
//播放网络视频
// mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
// @Override
// public void onPrepared(MediaPlayer mp) {
// mediaPlayer.start();
// }
// });
// try {
// mediaPlayer.setDataSource("/storage/emulated/0/Movies/qq.mp4");
// mediaPlayer.prepareAsync();
// }
// catch (IOException e) {
// Toast.makeText(this, "无法打开视频源", Toast.LENGTH_LONG).show();
// }
windowManager.addView(displayView, layoutParams);//添加悬浮窗口
}
}
//实现移动窗口
private class FloatingOnTouchListener implements View.OnTouchListener {
private int x;
private int y;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = (int) event.getRawX();
y = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int nowX = (int) event.getRawX();
int nowY = (int) event.getRawY();
int movedX = nowX - x;
int movedY = nowY - y;
x = nowX;
y = nowY;
layoutParams.x = layoutParams.x + movedX;
layoutParams.y = layoutParams.y + movedY;
windowManager.updateViewLayout(view, layoutParams);
break;
default:
break;
}
return true;
}
}
private void record(){
//获取系统时间
Date date = Calendar.getInstance().getTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
//根目录创建文件夹
File path = new File(Environment.getExternalStorageDirectory()+"/000AAAAAAMyvideo1/");
if(!path.exists()){
path.mkdir();
}
String fileName = sdf.format(date)+".mp4";
videoFile = new File(path,fileName);//保存地址
mediaRecorder = new MediaRecorder();
camera.setDisplayOrientation(90);//摄像头角度
camera.unlock();//解锁摄像头
mediaRecorder.setCamera(camera);//使用摄像头
mediaRecorder.reset();//重置参数
/**************************************自定义录制**************************************************************/
// mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//使用麦克风
// mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//使用摄像头获取对象
// mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//设置文件输出格式
// mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频编码格式
// mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置视频编码格式
// mediaRecorder.setVideoEncodingBitRate(5*1024*1024);
//mediaRecorder.setVideoSize(1920,1080);
//mediaRecorder.setVideoFrameRate(30);
// mediaRecorder.setOutputFile(videoFile.getAbsolutePath());//视频输出路径
// mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());//设置使用的surface
// mediaRecorder.setOrientationHint(90);//
/***********************************使用API高质量录制*******************************************/
CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);//使用麦克风
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//使用摄像头获取对象
mediaRecorder.setOutputFormat(mProfile.fileFormat);
mediaRecorder.setAudioEncoder(mProfile.audioCodec);
mediaRecorder.setVideoEncoder(mProfile.videoCodec);
mediaRecorder.setOutputFile(videoFile.getAbsolutePath());
mediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
//mediaRecorder.setVideoSize(1920, 1080);//设置会报错
mediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);
mediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);
mediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate);
mediaRecorder.setAudioChannels(mProfile.audioChannels);
mediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate);
mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());//设置使用的surface
try {
mediaRecorder.prepare();
mediaRecorder.start();
Toast.makeText(this,"START",Toast.LENGTH_SHORT).show();
idRecord = true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
悬浮窗布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/video_display_surfaceview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
我测试的手机版本8.0版本,手机API须高于24才能运行。
源码文件:https://download.csdn.net/download/qq_22969911/16071893