如果你想创建像后台录像机这样的录音应用程序
那么你需要使用 IntentService 而不是 Service 因为 IntentService 在后台线程中运行。
public class RecorderService extends Service implements SurfaceHolder.Callback {
private WindowManager windowManager;
private SurfaceView surfaceView;
private Camera camera = null;
private MediaRecorder mediaRecorder = null;
private String fileName = "";
private StopServiceBroadcast stopServiceBroadcast;
private String endRecordingDate;
private String endRecordingTime;
private SettingsModel settingsModel;
private Handler enableViewsHandler;
private Runnable runnable;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onCreate() {
settingsModel = SettingsModel.findById(SettingsModel.class, 1L);
setupSurfaceViewForRecording();
}
private void setupSurfaceViewForRecording() {
int LAYOUT_FLAG;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
}
// Create new SurfaceView, set its size to 1x1, move it to the top left corner and set this service as a callback
windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
surfaceView = new SurfaceView(this);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
1, 1,
// WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
// WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
LAYOUT_FLAG,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
);
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowManager.addView(surfaceView, layoutParams);
surfaceView.getHolder().addCallback(this);
}
private void registerBroadcastToStopRec() {
stopServiceBroadcast = new StopServiceBroadcast();
IntentFilter filter = new IntentFilter("stop_recording_action");
registerReceiver(stopServiceBroadcast, filter);
}
// Method called right after Surface created (initializing and starting MediaRecorder)
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mediaRecorder = new MediaRecorder();
setupCameraSide();
// if (settingsModel.getCameraSide().equalsIgnoreCase("front") && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
// Toast.makeText(this, R.string.front_camera_is_not_available_, Toast.LENGTH_SHORT).show();
// if (SharedPreferenceUtility.getInstance().getBooleanPreference(PreferenceKeys.IS_APP_RUNNING))
// fabStartStopRec.setEnabled(true);
// this.stopSelf();
// return;
// } else if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// Toast.makeText(this, R.string.back_camera_is_not_available_, Toast.LENGTH_SHORT).show();
// if (SharedPreferenceUtility.getInstance().getBooleanPreference(PreferenceKeys.IS_APP_RUNNING))
// fabStartStopRec.setEnabled(true);
// this.stopSelf();
// return;
// }
//______ check if camera is not available ___________
if (camera == null) {
if (settingsModel.getCameraSide().equals("front"))
Toast.makeText(this, R.string.front_camera_is_not_available_, Toast.LENGTH_SHORT).show();
else
Toast.makeText(this, R.string.back_camera_is_not_available_, Toast.LENGTH_SHORT).show();
if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, true))
fabStartStopRec.setEnabled(true);
this.stopSelf();
return;
}
notificationToShowRecordingStarted();
setupCameraParametersForRecording();
setupCameraOrientation();
setupAutoStopRecordingFactors();
setupRingingAndVibrationOnStartRec();
try {
//mCamera.setDisplayOrientation(90);
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
camera.unlock();
mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
mediaRecorder.setCamera(camera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
setupVideoQuality();
setupVideoStorage();
startRecording();
}
private void setupRingingAndVibrationOnStartRec() {
if (settingsModel.isRingsOnRecStarts()) {
Uri ringTuneUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.rec_start_sound);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), ringTuneUri);
r.play();
} else {
Camera.CameraInfo info = new Camera.CameraInfo();
if (info.canDisableShutterSound) {
camera.enableShutterSound(false);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// to mute shutter sound of camera
final AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mgr.setStreamMute(AudioManager.STREAM_SYSTEM, true);
mgr.setStreamSolo(AudioManager.STREAM_SYSTEM, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!mgr.isStreamMute(AudioManager.STREAM_SYSTEM))
mgr.setStreamMute(AudioManager.STREAM_MUSIC, true);
}
} else {
}
}
if (settingsModel.isVibrateOnRecStarts()) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
} else
v.vibrate(500);
}
}
private void setupVideoQuality() {
if (settingsModel.getCameraSide().equals("front")) {
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT)
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P));
else
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P/*settingsModel.getVideoQuality()*/));/*hot fix 1*/
} else
mediaRecorder.setProfile(CamcorderProfile.get(settingsModel.getVideoQuality()));
// if (settingsModel.getCameraSide().equals("front") &&
// (settingsModel.getVideoQuality() == CamcorderProfile.QUALITY_HIGH || settingsModel.getVideoQuality() == CamcorderProfile.QUALITY_1080P)) {
// mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));
// } else
// mediaRecorder.setProfile(CamcorderProfile.get(settingsModel.getVideoQuality()));
}
private int getVideoQualityWRTCameraCapacity(int videoQuality) {
switch (videoQuality) {
case CamcorderProfile.QUALITY_480P:
return CamcorderProfile.QUALITY_480P;
case CamcorderProfile.QUALITY_CIF:
return CamcorderProfile.QUALITY_CIF;
case CamcorderProfile.QUALITY_QCIF:
return CamcorderProfile.QUALITY_QCIF;
case CamcorderProfile.QUALITY_QVGA:
return CamcorderProfile.QUALITY_QVGA;
case CamcorderProfile.QUALITY_LOW:
return CamcorderProfile.QUALITY_LOW;
case CamcorderProfile.QUALITY_TIME_LAPSE_480P:
return CamcorderProfile.QUALITY_TIME_LAPSE_480P;
case CamcorderProfile.QUALITY_TIME_LAPSE_QCIF:
return CamcorderProfile.QUALITY_TIME_LAPSE_QCIF;
case CamcorderProfile.QUALITY_TIME_LAPSE_QVGA:
return CamcorderProfile.QUALITY_TIME_LAPSE_QVGA;
case CamcorderProfile.QUALITY_TIME_LAPSE_CIF:
return CamcorderProfile.QUALITY_TIME_LAPSE_CIF;
case CamcorderProfile.QUALITY_TIME_LAPSE_LOW:
return CamcorderProfile.QUALITY_TIME_LAPSE_LOW;
case CamcorderProfile.QUALITY_HIGH_SPEED_480P:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return CamcorderProfile.QUALITY_HIGH_SPEED_480P;
}
case CamcorderProfile.QUALITY_HIGH_SPEED_LOW:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return CamcorderProfile.QUALITY_HIGH_SPEED_LOW;
}
}
return CamcorderProfile.QUALITY_HIGH;
}
private void setupAutoStopRecordingFactors() {
if (settingsModel.isRecordingScheduled() && isValidTime(settingsModel.getWakeUpTimeForTimer())) {
mediaRecorder.setMaxDuration(settingsModel.getScheduleRecordingTimeDuration() * 60 * 1000);
if (settingsModel.isMaxFileSize())
mediaRecorder.setMaxFileSize(settingsModel.getMaxFileSizeLimit() * 1024 * 1024);
} else {
if (settingsModel.isLimitRecordingTime())
mediaRecorder.setMaxDuration(settingsModel.getRecordingTimeLimit() * 60 * 1000);
if (settingsModel.isMaxFileSize())
mediaRecorder.setMaxFileSize(settingsModel.getMaxFileSizeLimit() * 1024 * 1024);
}
mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
@Override
public void onInfo(MediaRecorder mediaRecorder, int i, int i1) {
if (i == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
stopSelf();
} else if (i == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
stopSelf();
}
}
});
}
public boolean isValidTime(long wakeUpTimeInMillis) {
Calendar smsTime = Calendar.getInstance();
smsTime.setTimeInMillis(wakeUpTimeInMillis);
Calendar now = Calendar.getInstance();
if (now.get(Calendar.DATE) == smsTime.get(Calendar.DATE)) {
if (now.get(Calendar.HOUR_OF_DAY) == smsTime.get(Calendar.HOUR_OF_DAY)) {
if (now.get(Calendar.MINUTE) == smsTime.get(Calendar.MINUTE))
return true;
}
}
return false;
}
private void setupCameraOrientation() {
if (settingsModel.getCameraSide().equals("front")) {
if (settingsModel.getCameraOrientation().equals("auto") || settingsModel.getCameraOrientation().equals("portrait")) {
mediaRecorder.setOrientationHint(270);
} else
mediaRecorder.setOrientationHint(0);
} else {
if (settingsModel.getCameraOrientation().equals("auto") || settingsModel.getCameraOrientation().equals("portrait")) {
mediaRecorder.setOrientationHint(90);
} else
mediaRecorder.setOrientationHint(180);
}
}
private void setupCameraParametersForRecording() {
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> ss = parameters.getSupportedVideoSizes();
Log.d("------ 0", ss.get(0).width + " " + ss.get(0).height);
Log.d("------ last", ss.get(ss.size() - 1).width + " " + ss.get(ss.size() - 1).height);
List<Camera.Size> sizes = parameters.getSupportedVideoSizes();
for (Camera.Size s : sizes)
Log.d("----", String.valueOf(s.width) + " * " + String.valueOf(s.height));
if (settingsModel.isFlashlightWhileRec() && !settingsModel.getCameraSide().equalsIgnoreCase("front"))
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
//_______ setup zoom factor ___________
if (settingsModel.isZoomEnabled()) {
if (parameters.isZoomSupported()) {
if (settingsModel.getZoomInValue() >= 0 && settingsModel.getZoomInValue() <= parameters.getMaxZoom()) {
parameters.setZoom(settingsModel.getZoomInValue());
}
}
}
camera.setParameters(parameters);
}
private void setupCameraSide() {
if (settingsModel.getCameraSide().equals("front"))
camera = openFrontFacingCamera();
else
camera = Camera.open();
}
private void startRecording() {
try {
mediaRecorder.prepare();
mediaRecorder.start();
saveRecStartingTime();
if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false))
updateViewsOnStartRec();
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void updateViewsOnStartRec() {
sflStartStopRecProgress.stopShimmerAnimation();
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.setText("00:00:00");
chronometer.start();
fabStartStopRec.setIcon(getResources().getDrawable(R.drawable.stop_icon), getResources().getDrawable(R.drawable.start_icon));
fabStartStopRec.showProgress(true);
fabStartStopRec.setIndeterminate(true);
runnable = new Runnable() {
@Override
public void run() {
fabStartStopRec.setEnabled(true);
}
};
enableViewsHandler = new Handler();
enableViewsHandler.postDelayed(runnable, 1000);
}
private void saveRecStartingTime() {
SharedPreferenceUtility.setPreference(this, PreferenceKeys.IS_RECORDING, true);
SharedPreferenceUtility.setPreference(this, PreferenceKeys.REC_STARTING_TIME, System.currentTimeMillis());
}
private void setupVideoStorage() {
//___________ creating new directory __________
File f = new File(settingsModel.getStoragePath(), settingsModel.getStorageFolderName());
if (!f.exists()) {
f.mkdirs();
}
if (isExternalStorageWritable()) {
try {
fileName = settingsModel.getStoragePath() + "/" + settingsModel.getStorageFolderName() + "/" +
DateFormat.format(settingsModel.getFileNameFormat(), new Date().getTime()) + ".mp4";
mediaRecorder.setOutputFile(fileName);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(this, R.string.you_dont_have_enough_space_, Toast.LENGTH_SHORT).show();
}
}
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
// Stop recording and remove SurfaceView
@Override
public void onDestroy() {
updateViewsOnStopRec();
updateSqlite();
//_________ if camera was not available __________
if (camera != null && mediaRecorder != null) {
final File file = new File(fileName);
saveAndScanFile(file.getAbsolutePath());
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
camera.lock();
camera.release();
Camera.CameraInfo info = new Camera.CameraInfo();
if (info.canDisableShutterSound) {
camera.enableShutterSound(true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// to mute shutter sound of camera
final AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mgr.setStreamMute(AudioManager.STREAM_SYSTEM, false);
mgr.setStreamSolo(AudioManager.STREAM_SYSTEM, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (mgr.isStreamMute(AudioManager.STREAM_MUSIC))
mgr.setStreamMute(AudioManager.STREAM_MUSIC, false);
}
} else {
}
/*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
// to unmute shutter sound of camera
AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mgr.setStreamMute(AudioManager.STREAM_SYSTEM, false);
} else {
Camera.CameraInfo info = new Camera.CameraInfo();
if (info.canDisableShutterSound) {
camera.enableShutterSound(true);
}
}*/
windowManager.removeView(surfaceView);
if (settingsModel.isShowNotificationOnRecStarts())
unregisterReceiver(stopServiceBroadcast);
if (settingsModel.isShowNotificationOnRecStops()) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
sendRecordedVideoNotification(file.getAbsolutePath());
}
}, 1000);
}
setupRiningAndVIberationOnStopRec();
if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false) && runnable != null)
enableViewsHandler.removeCallbacks(runnable);
}
}
private void setupRiningAndVIberationOnStopRec() {
if (settingsModel.isRingsOnRecStops()) {
Uri ringTuneUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.rec_end_sound);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), ringTuneUri);
r.play();
}
if (settingsModel.isVibrateOnRecStops()) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
} else
v.vibrate(500);
}
}
private void updateViewsOnStopRec() {
if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false)) {
if (chronometer != null)
chronometer.stop();
fabStartStopRec.setIcon(getResources().getDrawable(R.drawable.start_icon), getResources().getDrawable(R.drawable.stop_icon));
fabStartStopRec.showProgress(false);
fabStartStopRec.setIndeterminate(false);
if (sflStartStopRecProgress.isAnimationStarted())
sflStartStopRecProgress.stopShimmerAnimation();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void showCustomNotification(String fileName, String videoPath) {
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.single_expanded_notification_layout);
contentView.setTextViewText(R.id.tv_app_name, "Private Video Recorder");
contentView.setTextViewText(R.id.tv_video_name, fileName);
contentView.setImageViewBitmap(R.id.iv_video_image, getVideoThumbnail(videoPath));
Intent playVideoIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(videoPath));
playVideoIntent.setDataAndType(Uri.parse(videoPath), "video/mp4");
PendingIntent playVideoPendingIntent = PendingIntent.getActivity(this, 0, playVideoIntent, 0);
contentView.setOnClickPendingIntent(R.id.btn_play_video, playVideoPendingIntent);
contentView.setOnClickPendingIntent(R.id.btn_cancel, PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0, null));
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setCustomBigContentView(contentView);
Notification notification = mBuilder.build();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(1, notification);
}
private void notificationToShowRecordingStarted() {
registerBroadcastToStopRec();
Intent notificationIntent = new Intent("stop_recording_action");
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, notificationIntent, 0);
Notification notification = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
/*if (settingsModel.isShowNotificationOnRecStarts() || settingsModel.isRecordingScheduled() || !SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false)) {*/
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, createNotificationChannel());
notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.recording_running_icon)
.setContentTitle(getString(R.string.private_video_recorder))
.setContentText(getString(R.string.click_to_stop_recording))
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentIntent(pendingIntent)
.build();
int REC_STARTS_NOTIFICATION_ID = 123;
startForeground(REC_STARTS_NOTIFICATION_ID, notification);
/*}*/
} else {
if (settingsModel.isShowNotificationOnRecStarts()) {
notification = new Notification.Builder(this)
.setSound(null)
.setVibrate(null)
.setContentTitle(getString(R.string.private_video_recorder))
.setContentText(getString(R.string.click_to_stop_recording))
.setSmallIcon(R.mipmap.recording_running_icon)
.setContentIntent(pendingIntent)
.build();
int REC_STARTS_NOTIFICATION_ID = 123;
startForeground(REC_STARTS_NOTIFICATION_ID, notification);
}
}
// notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// notification.flags |= Notification.FLAG_AUTO_CANCEL;
/*int REC_STARTS_NOTIFICATION_ID = 123;
startForeground(REC_STARTS_NOTIFICATION_ID, notification);*/
}
private Camera openFrontFacingCamera() {
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras();
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
try {
camera = Camera.open(camIdx);
} catch (RuntimeException e) {
Toast.makeText(this, getString(R.string.camera_failed_to_open_) + e.toString(), Toast.LENGTH_SHORT).show();
}
}
}
return camera;
}
private void updateSqlite() {
SettingsModel settingsModel = SettingsModel.findById(SettingsModel.class, 1L);
settingsModel.setRecordingScheduled(false);
settingsModel.save();
SharedPreferenceUtility.setPreference(this, PreferenceKeys.IS_RECORDING, false);
}
}