diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 702c638..4a5bf1f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -59,6 +59,13 @@
+
+
+ //录音权限
+ private lateinit var micLauncher: ActivityResultLauncher
+ companion object{
+
+ const val key_with_audio="key_with_audio"
+ }
+ private var withAudio = false
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Common.setStatusBarTextColor(this, true)
+ withAudio = intent.getBooleanExtra(key_with_audio, false)
+ screenCaptureLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) { result ->
+ if (result.resultCode == RESULT_OK && result.data != null) {
+ val data: Intent? = result.data
+ FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
+ }
+ finish()
+ }
+
+ micLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted ->
+ if (isGranted) {
+ Common.showLog("mic 权限授予")
+ requestScreenPermission()
+ } else {
+ Common.showLog("mic 权限拒绝")
+ }
+ }
+ if(withAudio){
+ micLauncher.launch(android.Manifest.permission.RECORD_AUDIO)
+ }else{
+ requestScreenPermission()
+ }
+ }
+
+
+ private fun requestScreenPermission() {
+ val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
+ val intent = mediaProjectionManager.createScreenCaptureIntent()
+ screenCaptureLauncher.launch(intent)
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/adapter/ViewPager2Adapter.kt b/app/src/main/java/com/audio/record/screen/test/adapter/ViewPager2Adapter.kt
index 3984667..e48fa28 100644
--- a/app/src/main/java/com/audio/record/screen/test/adapter/ViewPager2Adapter.kt
+++ b/app/src/main/java/com/audio/record/screen/test/adapter/ViewPager2Adapter.kt
@@ -4,14 +4,14 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.audio.record.screen.test.fragment.MainFragment
-import com.audio.record.screen.test.fragment.RecorderFragment
+import com.audio.record.screen.test.fragment.RecordMainFragment
class ViewPager2Adapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
private val fragments: List = arrayListOf(
MainFragment.newInstance(),
- RecorderFragment.newInstance(),
+ RecordMainFragment.newInstance(),
MainFragment.newInstance()
)
override fun getItemCount(): Int = fragments.size
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/RecorderFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/RecordMainFragment.kt
similarity index 83%
rename from app/src/main/java/com/audio/record/screen/test/fragment/RecorderFragment.kt
rename to app/src/main/java/com/audio/record/screen/test/fragment/RecordMainFragment.kt
index 365308d..7c7bb0b 100644
--- a/app/src/main/java/com/audio/record/screen/test/fragment/RecorderFragment.kt
+++ b/app/src/main/java/com/audio/record/screen/test/fragment/RecordMainFragment.kt
@@ -4,19 +4,16 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.navigation.NavController
-import androidx.navigation.NavDestination
import androidx.navigation.fragment.NavHostFragment
import com.audio.record.screen.test.R
import com.audio.record.screen.test.base.BaseFragment
import com.audio.record.screen.test.databinding.FragmentRecordBinding
-import com.audio.record.screen.test.tool.Common
-class RecorderFragment : BaseFragment() {
+class RecordMainFragment : BaseFragment() {
companion object {
@JvmStatic
fun newInstance() =
- RecorderFragment().apply {
+ RecordMainFragment().apply {
}
}
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordNormalFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordNormalFragment.kt
index 0222f9a..55dff58 100644
--- a/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordNormalFragment.kt
+++ b/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordNormalFragment.kt
@@ -10,7 +10,10 @@ import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import com.audio.record.screen.test.R
@@ -21,16 +24,18 @@ import com.audio.record.screen.test.dialog.DialogPermission
import com.audio.record.screen.test.service.FloatingCallback
import com.audio.record.screen.test.service.FloatingWindowBridge
import com.audio.record.screen.test.tool.Common
+import com.audio.record.screen.test.tool.ConstValue
import com.audio.record.screen.test.tool.Permission
import com.audio.record.screen.test.tool.ScreenCaptureHelper
import com.audio.record.screen.test.viewmodel.MainViewModel
import com.audio.record.screen.test.viewmodel.PreviewViewModel
+import kotlinx.coroutines.launch
class RecordNormalFragment : BaseFragment(), FloatingCallback {
private lateinit var navController: NavController
- private val REQUEST_SCREENSHOT = 125
+
private lateinit var viewModel: MainViewModel
private lateinit var mediaProjectionManager: MediaProjectionManager
@@ -51,6 +56,9 @@ class RecordNormalFragment : BaseFragment(), Floati
//是否带音频录制
private var isWithAudio = false
+
+ private var pendingType: Int = -1
+ private val key_type = "check_type"
override fun initBinding(
inflater: LayoutInflater,
container: ViewGroup?
@@ -115,7 +123,7 @@ class RecordNormalFragment : BaseFragment(), Floati
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
- FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
+
} else {
Common.showLog("用户取消了录屏权限授权")
}
@@ -125,10 +133,16 @@ class RecordNormalFragment : BaseFragment(), Floati
recorderLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK && result.data != null) {
- val data = result.data
+ val intent = result.data
val resultCode = result.resultCode
- FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
- startCountDown()
+ FloatingWindowBridge.updateMediaProjection(result.resultCode, intent!!)
+ when(pendingType){
+ ConstValue.type_record->{ startCountDown()}
+ ConstValue.type_show_screenshot->{
+ FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
+ }
+ }
+
} else {
Common.showLog("录屏授权失败 ")
}
@@ -196,10 +210,7 @@ class RecordNormalFragment : BaseFragment(), Floati
private fun setScreenshot(boolean: Boolean) {
if (boolean) {
- if (FloatingWindowBridge.getMediaProjection() == null) {
- val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
- screenCaptureLauncher.launch(captureIntent)
- } else {
+ requestRecordPermission(ConstValue.type_show_screenshot){
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
}
@@ -217,14 +228,28 @@ class RecordNormalFragment : BaseFragment(), Floati
}
private fun startRecorder() {
- if (FloatingWindowBridge.getMediaProjection() == null) {
- val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
- recorderLauncher.launch(captureIntent)
- } else {
+ requestRecordPermission( ConstValue.type_record){
startCountDown()
}
}
+
+ /**
+ * 请求录屏权限
+ */
+ private fun requestRecordPermission(type:Int,check:(()->Unit)? = null){
+ // TODO: requestRecordPermission
+ if (FloatingWindowBridge.getMediaProjection() == null) {
+ val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
+ pendingType = type
+ recorderLauncher.launch(captureIntent)
+ }else{
+ check?.invoke()
+ }
+ }
+
+
+
private fun showAudioDialog() {
mAudioDialog = mAudioDialog ?: DialogAudio {
when (it) {
@@ -249,7 +274,22 @@ class RecordNormalFragment : BaseFragment(), Floati
}
override fun onStartRecording() {
- navController.navigate(R.id.action_to_recording)
+ Common.showLog(" FragmentRecordNormal 回调 onStartRecording")
+ if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ navController.navigate(R.id.action_to_recording)
+ Common.showLog(" FragmentRecordNormal 回调 onStartRecording 执行111111111")
+ } else {
+ Common.showLog(" FragmentRecordNormal 回调 onStartRecording 延迟执行")
+ // 延迟执行
+ lifecycleScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ Common.showLog(" FragmentRecordNormal 回调 onStartRecording 延迟执行 11111111")
+ navController.navigate(R.id.action_to_recording)
+ }
+ }
+ }
+
+
}
diff --git a/app/src/main/java/com/audio/record/screen/test/service/FloatingCallback.kt b/app/src/main/java/com/audio/record/screen/test/service/FloatingCallback.kt
index c3fe21b..550877e 100644
--- a/app/src/main/java/com/audio/record/screen/test/service/FloatingCallback.kt
+++ b/app/src/main/java/com/audio/record/screen/test/service/FloatingCallback.kt
@@ -7,4 +7,6 @@ interface FloatingCallback {
fun onUpdateRecordTime(time:String){}
fun onStopRecord(){}
+
+
}
diff --git a/app/src/main/java/com/audio/record/screen/test/service/FloatingWindowBridge.kt b/app/src/main/java/com/audio/record/screen/test/service/FloatingWindowBridge.kt
index 4271e07..6e84a13 100644
--- a/app/src/main/java/com/audio/record/screen/test/service/FloatingWindowBridge.kt
+++ b/app/src/main/java/com/audio/record/screen/test/service/FloatingWindowBridge.kt
@@ -80,6 +80,7 @@ object FloatingWindowBridge {
}
fun sendCommand(command: String, countdownValues: Array? = null) {
+ Common.showLog("-------------command=${command}")
when (command) {
COMMEND_hide_camera -> service?.hideFrontCamera()
COMMEND_show_camera -> service?.showCameraView()
diff --git a/app/src/main/java/com/audio/record/screen/test/service/RecordState.kt b/app/src/main/java/com/audio/record/screen/test/service/RecordState.kt
new file mode 100644
index 0000000..b61f47b
--- /dev/null
+++ b/app/src/main/java/com/audio/record/screen/test/service/RecordState.kt
@@ -0,0 +1,7 @@
+package com.audio.record.screen.test.service
+
+
+enum class RecordState {
+ DEFAULT,
+ RECORDING,
+ PAUSE }
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/service/ScreenRecordService.kt b/app/src/main/java/com/audio/record/screen/test/service/ScreenRecordService.kt
index bb1f53a..1df6a64 100644
--- a/app/src/main/java/com/audio/record/screen/test/service/ScreenRecordService.kt
+++ b/app/src/main/java/com/audio/record/screen/test/service/ScreenRecordService.kt
@@ -5,7 +5,6 @@ import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
-import android.content.ContentUris
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
@@ -17,12 +16,11 @@ import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.Binder
import android.os.Build
-import android.os.Environment
+import android.os.CountDownTimer
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.ParcelFileDescriptor
-import android.provider.MediaStore
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@@ -30,6 +28,7 @@ import android.view.WindowManager
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
+import android.widget.LinearLayout
import android.widget.RemoteViews
import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher
@@ -39,21 +38,26 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
-import androidx.core.content.FileProvider
+import androidx.core.view.isVisible
import com.audio.record.screen.test.R
import com.audio.record.screen.test.activity.PlayActivity
+import com.audio.record.screen.test.activity.ScreenPermissionActivity
import com.audio.record.screen.test.tool.Common
+import com.audio.record.screen.test.tool.ConstValue
import com.audio.record.screen.test.tool.DraggableViewHelper
+import com.audio.record.screen.test.tool.Extend.dpToPx
+import com.audio.record.screen.test.tool.Extend.pxToDp
import com.audio.record.screen.test.tool.ScreenCaptureHelper
import com.audio.record.screen.test.tool.VideoFileHelper
import com.audio.record.screen.test.view.CountDownFloatingManager
-import java.io.File
import java.lang.ref.WeakReference
class ScreenRecordService : Service() {
+
private var lifecycleOwner: CustomLifecycleOwner? = null
private var isBallViewAdded = false
+ private var isBallExpandViewAdded = false
private var isScreenshotViewAdded = false
private var isWebcamViewAdded = false
private var isRecordViewAdded = false
@@ -64,6 +68,17 @@ class ScreenRecordService : Service() {
//悬浮球View
private var ballView: View? = null
+ private var imIconVideo: ImageView? = null
+ private var smallLayoutRecording: LinearLayout? = null
+ private var smallTvTime: TextView? = null
+
+
+ //悬浮球展开View
+ private var ballViewExpand: View? = null
+ private var imRecord: ImageView? = null
+ private var tvRecordTime: TextView? = null
+ private var imNoAudioRecord: ImageView? = null
+ private var imScreenshot:ImageView? = null
//截屏View
private var screenshotView: View? = null
@@ -74,9 +89,10 @@ class ScreenRecordService : Service() {
private var index = 0
//录屏成功View
- private var recordView: View? = null
+ private var recordCompleteView: View? = null
private val windowManager by lazy { getSystemService(WINDOW_SERVICE) as WindowManager }
+ private lateinit var layoutParamsBallExpand: WindowManager.LayoutParams
private lateinit var layoutParamsBall: WindowManager.LayoutParams
private lateinit var layoutParamsScreenshot: WindowManager.LayoutParams
private lateinit var layoutParamsCameraView: WindowManager.LayoutParams
@@ -93,17 +109,22 @@ class ScreenRecordService : Service() {
private var callbackRef: FloatingCallback? = null
//截屏
-
var mIntent: Intent? = null
private var mCode: Int = 0
private lateinit var mediaRecorder: MediaRecorder
private lateinit var tmpVideoUri: Uri
+ private lateinit var videoName: String
private var tmpVideoPfd: ParcelFileDescriptor? = null
+
+ //当前录制是否带音频
private var isWithAudio = false
private val callbacks = mutableListOf>()
+ private var countDownTimer: CountDownTimer? = null
+ //录制状态标志 true 录制中
+ private var mRecordingStatus = -1
//录制时间监听相关
private var recordingStartTime: Long = 0L // 本次录制开始时间
@@ -116,7 +137,10 @@ class ScreenRecordService : Service() {
private var isPause = false
private lateinit var virtualDisplay: VirtualDisplay
- private var mediaProjection:MediaProjection? = null
+ //悬浮球点击进行的动作 type_record_audio/type_record_without_audio/type_screenshot
+ private var ballType: Int? = null
+
+ private var mediaProjection: MediaProjection? = null
private val timeUpdateRunnable = object : Runnable {
override fun run() {
@@ -125,6 +149,8 @@ class ScreenRecordService : Service() {
System.currentTimeMillis() - recordingStartTime - totalPausedTime
val elapsed = currentRecordingTimeInMillis / 1000
val onRecordingTimeChanged = Common.onRecordingTimeChanged(elapsed)
+ tvRecordTime?.text = onRecordingTimeChanged
+ smallTvTime?.text = onRecordingTimeChanged
callbacks.forEach {
it.get()?.onUpdateRecordTime(onRecordingTimeChanged)
}
@@ -180,9 +206,26 @@ class ScreenRecordService : Service() {
mIntent = m
mCode = code
// mediaProjection = mediaProjectionManager.getMediaProjection(code, m)
- }
- fun resetIntent(){
- mIntent = null
+ ballType?.let {
+ when (it) {
+ ConstValue.type_record_without_audio -> {
+ isWithAudio = false
+ val arrayOf = arrayOf("3", "2", "1")
+ showCountDownView(arrayOf)
+ }
+
+ ConstValue.type_record_audio -> {
+ isWithAudio = true
+ val arrayOf = arrayOf("3", "2", "1")
+ showCountDownView(arrayOf)
+ }
+
+ ConstValue.type_screenshot -> {
+ startScreenshot()
+ }
+ }
+ ballType = null
+ }
}
@@ -224,6 +267,22 @@ class ScreenRecordService : Service() {
.build()
}
+
+ /**
+ * 开始截屏
+ */
+ private fun startScreenshot(){
+ // TODO: 开始截屏
+ mIntent?.let { intent->
+ mediaProjectionManager.getMediaProjection(mCode, intent)?.let { mediaProjection->
+ hideScreenshot()
+ ScreenCaptureHelper.startScreenCapture(this, mediaProjection) {
+ //截屏完成
+ showScreenshot()
+ }
+ }
+ }
+ }
@SuppressLint("ClickableViewAccessibility")
fun showCameraView() {
if (frontCameraView == null) {
@@ -255,6 +314,10 @@ class ScreenRecordService : Service() {
hideFrontCamera() // 自动清除
hideBall()
hideScreenshot()
+ hideBallExpand()
+ hideRecordCompleteView()
+ ballViewExpand = null
+ recordCompleteView = null
frontCameraView = null
ballView = null
screenshotView = null
@@ -298,9 +361,15 @@ class ScreenRecordService : Service() {
layoutParamsBall.gravity = Gravity.TOP or Gravity.START
layoutParamsBall.x = screenWH.first - size - 20
layoutParamsBall.y = screenWH.second / 2 - size.div(2) // 居中
+ smallLayoutRecording = ballView!!.findViewById(R.id.layout_recording)
+ imIconVideo = ballView!!.findViewById(R.id.image)
+ smallTvTime = ballView!!.findViewById(R.id.tv_small_time)
- DraggableViewHelper.attachToWindow(ballView!!, layoutParamsBall, windowManager, true)
+ DraggableViewHelper.attachToWindow(ballView!!, layoutParamsBall, windowManager, true) {
+ hideBall()
+ showBallExpand()
+ }
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
}
if (!isBallViewAdded) {
@@ -310,6 +379,116 @@ class ScreenRecordService : Service() {
}
+ fun showBallExpand() {
+ if (ballViewExpand == null) {
+ ballViewExpand = LayoutInflater.from(this).inflate(R.layout.floating_ball_expand, null)
+ layoutParamsBallExpand = getLayoutParams()
+ layoutParamsBallExpand.gravity = Gravity.TOP or Gravity.START
+ imRecord = ballViewExpand!!.findViewById(R.id.ic_record)
+ tvRecordTime = ballViewExpand!!.findViewById(R.id.tv_record_time)
+ imNoAudioRecord = ballViewExpand!!.findViewById(R.id.ic_without_audio)
+ imScreenshot = ballViewExpand!!.findViewById(R.id.ic_screenshot)
+
+ imRecord?.setOnClickListener {
+ it.isSelected.let { select ->
+ if (!select) {
+ //录制带音频的视频
+ ballType = ConstValue.type_record_audio
+ intentPermission(true)
+ } else {
+ //录制完成
+ stopRecording()
+ }
+ }
+ }
+ imNoAudioRecord?.setOnClickListener {
+ // TODO:
+ when (mRecordingStatus) {
+ ConstValue.status_recording -> {
+ //暂停录制
+ pauseRecording()
+ }
+
+ ConstValue.status_pause -> {
+ //继续录制
+ resumeRecording()
+ }
+
+ else -> {
+ // 开始录制没有声音的视频
+ ballType = ConstValue.type_record_without_audio
+ intentPermission(false)
+ }
+ }
+
+ }
+
+ imScreenshot?.setOnClickListener {
+ ballType = ConstValue.type_screenshot
+ intentPermission(false)
+
+ }
+ }
+
+ if (!isBallExpandViewAdded) {
+ val size = Common.dpToPx(36, this)
+ val aWidthPx = Common.dpToPx(55, this)
+ val aHeightPx = Common.dpToPx(250, this)
+ // 转换 B 的宽高为像素
+ val bWidthPx = size
+ val bHeightPx = size
+
+ // 计算 B 的中心点坐标
+ val bCenterX = layoutParamsBall.x + bWidthPx / 2
+ val bCenterY = layoutParamsBall.y + bHeightPx / 2
+
+ // 让 A 的左上角从 B 的中心点出发(居中显示)
+ var aX = bCenterX - aWidthPx / 2
+ var aY = bCenterY - aHeightPx / 2
+
+ val (screenWidth, screenHeight) = Common.getScreenWH(this)
+ // 修正边界:防止 A 悬浮窗超出屏幕边界
+ aX = aX.coerceIn(0, screenWidth - aWidthPx)
+ aY = aY.coerceIn(0, screenHeight - aHeightPx)
+
+ if (aX >= screenWidth/2) {
+ layoutParamsBallExpand.x = aX - 10.dpToPx(this)
+ Common.showLog("--111111111--aX=${aX} layoutParamsBallExpand.x=${ layoutParamsBallExpand.x}")
+ } else {
+ layoutParamsBallExpand.x = aX + 10.dpToPx(this)
+ Common.showLog("---22222222--aX=${aX} layoutParamsBallExpand.x=${ layoutParamsBallExpand.x}")
+ }
+ layoutParamsBallExpand.y = aY
+ windowManager.addView(ballViewExpand, layoutParamsBallExpand)
+ isBallExpandViewAdded = true
+ delayRemove {
+ Common.showLog("-------切换ball显示")
+ hideBallExpand()
+ showBall()
+ }
+ }
+ }
+
+
+ /**
+ * 3秒后自动隐藏
+ */
+ private fun delayRemove(onFinish: (() -> Unit)?) {
+ countDownTimer?.cancel()
+ countDownTimer = object : CountDownTimer(3000L, 1000L) {
+ override fun onTick(millisUntilFinished: Long) {
+
+ }
+
+ override fun onFinish() {
+ onFinish?.invoke()
+ countDownTimer = null
+ }
+ }
+ countDownTimer?.start()
+ }
+
+
//显示截屏
fun showScreenshot() {
if (screenshotView == null) {
@@ -327,17 +506,7 @@ class ScreenRecordService : Service() {
windowManager,
true
) {
-
- Common.showLog("--------------callbackRef=${callbackRef}")
-// callbackRef?.onFloatingButtonClicked(FloatingWindowBridge.CLICK_screenshot)
- mIntent?.let {
- mediaProjectionManager.getMediaProjection(mCode, it)?.let {
- hideScreenshot()
- ScreenCaptureHelper.startScreenCapture(this, it) {
-// showScreenshot()
- }
- }
- }
+ startScreenshot()
}
}
if (!isScreenshotViewAdded) {
@@ -349,16 +518,21 @@ class ScreenRecordService : Service() {
//显示截屏
fun showRecordView() {
- if (recordView == null) {
- recordView = LayoutInflater.from(this).inflate(R.layout.floating_record_complete, null)
+ if (recordCompleteView == null) {
+ recordCompleteView =
+ LayoutInflater.from(this).inflate(R.layout.floating_record_complete, null)
layoutParamsRecordView = getLayoutParams()
layoutParamsRecordView.gravity = Gravity.CENTER
- val imClose = recordView!!.findViewById(R.id.close).setOnClickListener {
- hideRecordView()
- }
- recordView!!.findViewById(R.id.layout_video).setOnClickListener {
+ val imClose =
+ recordCompleteView!!.findViewById(R.id.close).setOnClickListener {
+ hideRecordCompleteView()
+ }
+ recordCompleteView!!.findViewById(R.id.layout_video).setOnClickListener {
// TODO: 跳到播放页面
+ hideRecordCompleteView()
val intent = Intent(this, PlayActivity::class.java).apply {
+ putExtra(PlayActivity.KEY_URI, tmpVideoUri)
+ putExtra(PlayActivity.KEY_name, videoName)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
this.startActivity(intent)
@@ -366,15 +540,15 @@ class ScreenRecordService : Service() {
}
}
if (!isRecordViewAdded) {
-
val videoThumbnail = Common.getVideoThumbnail(tmpVideoPfd!!)
Common.showLog("--------------videoThumbnail=${videoThumbnail}")
- val thumb = recordView!!.findViewById(R.id.image)
-
+ val thumb = recordCompleteView!!.findViewById(R.id.image)
thumb.setImageBitmap(videoThumbnail)
-
- windowManager.addView(recordView, layoutParamsRecordView)
+ windowManager.addView(recordCompleteView, layoutParamsRecordView)
isRecordViewAdded = true
+ delayRemove {
+ hideRecordCompleteView()
+ }
}
}
@@ -392,9 +566,23 @@ class ScreenRecordService : Service() {
// frontCameraView = null
}
+ fun hideBallExpand() {
+ ballViewExpand?.let {
+ try {
+ if (isBallExpandViewAdded) {
+ windowManager.removeView(it)
+ isBallExpandViewAdded = false
+ }
+
+ } catch (_: Exception) {
+ }
+ }
+// frontCameraView = null
+ }
+
//隐藏录制预览View
- fun hideRecordView() {
- recordView?.let {
+ fun hideRecordCompleteView() {
+ recordCompleteView?.let {
try {
if (isRecordViewAdded) {
windowManager.removeView(it)
@@ -451,7 +639,7 @@ class ScreenRecordService : Service() {
hideCountDown()
mIntent?.let { intent ->
mediaProjectionManager.getMediaProjection(mCode, intent)
- ?.let { media->
+ ?.let { media ->
mediaProjection = media
startRecording()
callbacks.forEach {
@@ -516,11 +704,14 @@ class ScreenRecordService : Service() {
*/
fun startRecording() {
Common.showLog("-------录屏中.....")
+ // TODO: 录屏中.
val fullScreenSize = Common.getFullScreenSize(this)
val width = VideoFileHelper.alignTo16(fullScreenSize.first)
val height = VideoFileHelper.alignTo16(fullScreenSize.second)
initRecorder(width, height)
mediaRecorder.start()
+ mRecordingStatus = ConstValue.status_recording
+ updateBall()
recordingStartTime = System.currentTimeMillis()
totalPausedTime = 0L
mRecorderHandler.post(timeUpdateRunnable)
@@ -556,8 +747,6 @@ class ScreenRecordService : Service() {
}, null
)
}
-
-
}
@@ -600,8 +789,11 @@ class ScreenRecordService : Service() {
* 暂停录制视频
*/
fun pauseRecording() {
+ // TODO: 暂停录制视频
Common.showLog("-------暂停.....")
mediaRecorder.pause()
+ mRecordingStatus = ConstValue.status_pause
+ updateBall()
pauseStartTime = System.currentTimeMillis()
isPause = true
}
@@ -610,15 +802,67 @@ class ScreenRecordService : Service() {
* 继续录制视频
*/
fun resumeRecording() {
+ // TODO: 继续录制视频
Common.showLog("------继续.....")
+
mediaRecorder.resume()
+ mRecordingStatus = ConstValue.status_recording
+ updateBall()
totalPausedTime += System.currentTimeMillis() - pauseStartTime
isPause = false
mRecorderHandler.post(timeUpdateRunnable)
}
+ /**
+ * 录制状态更新
+ */
+ private fun updateBall() {
+ // TODO: 录制状态更新
+ when (mRecordingStatus) {
+ ConstValue.status_recording -> {
+ imNoAudioRecord?.run {
+ isSelected = true
+ isActivated = false
+
+ }
+ imRecord?.isSelected = true
+ tvRecordTime?.isVisible = true
+
+ imIconVideo?.isVisible = false
+ smallLayoutRecording?.isVisible = true
+ }
+
+ ConstValue.status_pause -> {
+ imNoAudioRecord?.run {
+ isSelected = false
+ isActivated = true
+ }
+
+ imIconVideo?.isVisible = false
+ smallLayoutRecording?.isVisible = true
+ }
+
+ ConstValue.status_complete -> {
+ imNoAudioRecord?.run {
+ isSelected = false
+ isActivated = false
+ }
+
+ imRecord?.isSelected = false
+ tvRecordTime?.isVisible = false
+
+ imIconVideo?.isVisible = true
+ smallLayoutRecording?.isVisible = false
+ hideBallExpand()
+ showBall()
+ }
+ }
+ }
+
+
private fun initRecorder(width: Int, height: Int) {
- val (Uri, pfd) = VideoFileHelper.createVideoFile(this, Common.folderName)
+ videoName = System.currentTimeMillis().toString()
+ val (Uri, pfd) = VideoFileHelper.createVideoFile(this, Common.folderName, videoName)
tmpVideoUri = Uri
tmpVideoPfd = pfd
mediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -626,14 +870,18 @@ class ScreenRecordService : Service() {
} else {
MediaRecorder()
}.apply {
- if (isWithAudio)
+ if (isWithAudio) {
setAudioSource(MediaRecorder.AudioSource.MIC)
+ }
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setOutputFile(pfd?.fileDescriptor)
setVideoSize(width, height)
+ if (isWithAudio) {
+ setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
+ }
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
- setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
+
setVideoEncodingBitRate(8 * 1000 * 1000)
setVideoFrameRate(30)
prepare()
@@ -641,6 +889,9 @@ class ScreenRecordService : Service() {
}
fun stopRecording() {
+ // TODO: 录屏完成
+ mRecordingStatus = ConstValue.status_complete
+ updateBall()
Common.showLog("-------录屏完成.....")
mRecorderHandler.removeCallbacks(timeUpdateRunnable)
releaseAll()
@@ -669,4 +920,12 @@ class ScreenRecordService : Service() {
}
+ private fun intentPermission(requestAudio: Boolean) {
+ val intent = Intent(this, ScreenPermissionActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.putExtra(ScreenPermissionActivity.key_with_audio, requestAudio)
+ this.startActivity(intent)
+
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/tool/ConstValue.kt b/app/src/main/java/com/audio/record/screen/test/tool/ConstValue.kt
new file mode 100644
index 0000000..be7ea50
--- /dev/null
+++ b/app/src/main/java/com/audio/record/screen/test/tool/ConstValue.kt
@@ -0,0 +1,26 @@
+package com.audio.record.screen.test.tool
+
+object ConstValue {
+
+
+ //--------------RecordNormalFragment
+ //开始录制
+ const val type_record = 0
+
+ //显示截屏悬浮球
+ const val type_show_screenshot = 1
+
+
+
+ //--------------ScreenRecordService
+ //一次性使用
+ const val type_record_audio = 3
+ const val type_record_without_audio = 4
+ const val type_screenshot = 5
+
+
+ //当前状态
+ const val status_recording = 6 //录制当中
+ const val status_pause = 7 //录制暂停
+ const val status_complete = 8 //录制完成
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/tool/Extend.kt b/app/src/main/java/com/audio/record/screen/test/tool/Extend.kt
index 7ef813c..48f05b6 100644
--- a/app/src/main/java/com/audio/record/screen/test/tool/Extend.kt
+++ b/app/src/main/java/com/audio/record/screen/test/tool/Extend.kt
@@ -6,6 +6,9 @@ import android.media.Image
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
object Extend {
@@ -44,11 +47,18 @@ object Extend {
layoutParams = params
}
+ fun ImageView.setDrawableCompat(@DrawableRes resId: Int) {
+ val drawable = ContextCompat.getDrawable(context, resId)
+ this.setImageDrawable(drawable)
+ }
fun Int.dpToPx(context: Context): Int =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, this.toFloat(),
context.resources.displayMetrics
).toInt()
+ fun Context.pxToDp(px: Int): Float {
+ return px / resources.displayMetrics.density
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/tool/ScreenCaptureHelper.kt b/app/src/main/java/com/audio/record/screen/test/tool/ScreenCaptureHelper.kt
index 0381c1a..1ccf91d 100644
--- a/app/src/main/java/com/audio/record/screen/test/tool/ScreenCaptureHelper.kt
+++ b/app/src/main/java/com/audio/record/screen/test/tool/ScreenCaptureHelper.kt
@@ -68,8 +68,8 @@ object ScreenCaptureHelper {
if (image != null) {
val bitmap = imageToBitmap(image)
saveBitmap(context, bitmap, folderName)
- isOK.invoke()
showScreenshotPreviewAnimation(context, bitmap,width,height)
+ isOK.invoke()
image.close()
}
imageReader.close()
diff --git a/app/src/main/res/drawable/ball_audio_record.xml b/app/src/main/res/drawable/ball_audio_record.xml
new file mode 100644
index 0000000..0a0a8c2
--- /dev/null
+++ b/app/src/main/res/drawable/ball_audio_record.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ball_continue_recording.xml b/app/src/main/res/drawable/ball_continue_recording.xml
new file mode 100644
index 0000000..47420af
--- /dev/null
+++ b/app/src/main/res/drawable/ball_continue_recording.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ball_home.xml b/app/src/main/res/drawable/ball_home.xml
new file mode 100644
index 0000000..c56d94b
--- /dev/null
+++ b/app/src/main/res/drawable/ball_home.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ball_pause.xml b/app/src/main/res/drawable/ball_pause.xml
new file mode 100644
index 0000000..42991e7
--- /dev/null
+++ b/app/src/main/res/drawable/ball_pause.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ball_recording.xml b/app/src/main/res/drawable/ball_recording.xml
new file mode 100644
index 0000000..544a172
--- /dev/null
+++ b/app/src/main/res/drawable/ball_recording.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ball_screenshot.xml b/app/src/main/res/drawable/ball_screenshot.xml
new file mode 100644
index 0000000..87abd1a
--- /dev/null
+++ b/app/src/main/res/drawable/ball_screenshot.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ball_without_audio_record.xml b/app/src/main/res/drawable/ball_without_audio_record.xml
new file mode 100644
index 0000000..ee8da10
--- /dev/null
+++ b/app/src/main/res/drawable/ball_without_audio_record.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_ball.xml b/app/src/main/res/drawable/bg_ball.xml
new file mode 100644
index 0000000..ebb411b
--- /dev/null
+++ b/app/src/main/res/drawable/bg_ball.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/recording_bg_oval.xml b/app/src/main/res/drawable/recording_bg_oval.xml
new file mode 100644
index 0000000..2fd72a3
--- /dev/null
+++ b/app/src/main/res/drawable/recording_bg_oval.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/recording_oval_red.xml b/app/src/main/res/drawable/recording_oval_red.xml
new file mode 100644
index 0000000..fea773e
--- /dev/null
+++ b/app/src/main/res/drawable/recording_oval_red.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/status_ball_record.xml b/app/src/main/res/drawable/status_ball_record.xml
new file mode 100644
index 0000000..d359667
--- /dev/null
+++ b/app/src/main/res/drawable/status_ball_record.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/status_ball_record_three.xml b/app/src/main/res/drawable/status_ball_record_three.xml
new file mode 100644
index 0000000..4c92fa6
--- /dev/null
+++ b/app/src/main/res/drawable/status_ball_record_three.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/floating_ball.xml b/app/src/main/res/layout/floating_ball.xml
index 991260b..202dff4 100644
--- a/app/src/main/res/layout/floating_ball.xml
+++ b/app/src/main/res/layout/floating_ball.xml
@@ -9,4 +9,29 @@
android:layout_height="36dp"
android:src="@drawable/global_ball" />
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/floating_ball_expand.xml b/app/src/main/res/layout/floating_ball_expand.xml
new file mode 100644
index 0000000..f08d6b7
--- /dev/null
+++ b/app/src/main/res/layout/floating_ball_expand.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 16dcb64..099924a 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -6,4 +6,13 @@
+
+
+
+
\ No newline at end of file