diff --git a/app/RecordScreen.jks b/app/RecordScreen.jks
new file mode 100644
index 0000000..8a25b7d
Binary files /dev/null and b/app/RecordScreen.jks differ
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ab92d13..aa659de 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,25 +1,30 @@
+import java.util.Date
+import java.text.SimpleDateFormat
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
-
+val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android {
namespace = "com.audio.record.screen.test"
compileSdk = 35
defaultConfig {
- applicationId = "com.audio.record.screen.test"
+ applicationId = "com.audio.record.screen"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
-
+ setProperty(
+ "archivesBaseName",
+ "RecordScreen_V" + versionName + "(${versionCode})_$timestamp"
+ )
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
- isMinifyEnabled = false
+ isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
@@ -79,8 +84,8 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
- implementation(files("libs/jetified-ffmpeg-kit-full-6.0.aar"))
- implementation(files("libs/smart-exception-common-0.2.1.jar"))
- implementation(files("libs/smart-exception-java-0.2.1.jar"))
+// implementation(files("libs/jetified-ffmpeg-kit-full-6.0.aar"))
+// implementation(files("libs/smart-exception-common-0.2.1.jar"))
+// implementation(files("libs/smart-exception-java-0.2.1.jar"))
}
\ No newline at end of file
diff --git a/app/keystore.properties b/app/keystore.properties
new file mode 100644
index 0000000..2ba649e
--- /dev/null
+++ b/app/keystore.properties
@@ -0,0 +1,6 @@
+app_name=RecordScreen
+package_name=com.audio.record.screen
+keystoreFile=app/RecordScreen.jks
+key_alias=RecordScreenkey0
+key_store_password=RecordScreen
+key_password=RecordScreen
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 82e83c1..7020d3b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,10 +7,12 @@
android:required="false" />
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ android:exported="true"
+ android:screenOrientation="portrait">
@@ -48,28 +58,16 @@
android:name=".activity.ImageViewActivity"
android:exported="false"
android:screenOrientation="portrait" />
-
+
-
-
-
-
-
-
+ android:screenOrientation="portrait"/>
+
+
-
-
-
() {
- val NOTIFICATION_PERMISSION_REQUEST_CODE = 123
- val SCREEN_CAPTURE_REQUEST_CODE = 124
- val REQUEST_SCREENSHOT = 125
-
- lateinit var requestPermissionLauncher: ActivityResultLauncher
- lateinit var micPermissionLauncher: ActivityResultLauncher
- lateinit var cameraPermissionLauncher: ActivityResultLauncher
- lateinit var requestStoragePermission: ActivityResultLauncher
-
- lateinit var mediaProjectionManager: MediaProjectionManager
-// private var floatingService: ScreenRecordService? = null
-
- private lateinit var tmpVideoUri:Uri
-
-// private val connection = object : ServiceConnection {
-// override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
-// val binder = service as ScreenRecordService.FloatingBinder
-// floatingService = binder.getService()
-// binder.setCallback(object : FloatingCallback {
-// override fun onFloatingButtonClicked(action: String) {
-// // 处理来自悬浮窗的点击
-// when (action) {
-// "stop_clicked" -> {
-//
-// }
-// }
-// }
-// })
-// }
-//
-// override fun onServiceDisconnected(name: ComponentName?) {
-// floatingService = null
-// }
-// }
-
-
- override fun initBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
-
- override fun getFullColor(): Boolean? = true
-
- override fun onCreateInit() {
- initPermissionLauncher()
-// checkSyswindow(this@MainActivity)
- checkStoragePermissionAndDoSomething()
-
-
- binding.btn1.setOnClickListener {
- requestNotification()
- }
- binding.btn2.setOnClickListener {
- checkStoragePermissionAndDoSomething()
- }
- binding.btn3.setOnClickListener {
- micPermissionLauncher.launch(android.Manifest.permission.RECORD_AUDIO)
- }
- binding.btn4.setOnClickListener {
- checkCamera()
- }
- binding.btnShowcamera.setOnClickListener {
- it.isSelected = !it.isSelected
- if(it.isSelected){
- FloatingWindowBridge.sendCommand("show")
- }else{
- FloatingWindowBridge.sendCommand("hide")
- }
-
- }
- binding.btn7.setOnClickListener {
- startActivity(Intent(this,PreviewActivity::class.java))
-
- }
-
- mediaProjectionManager =
- getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
- binding.btn5.setOnClickListener {
- startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), SCREEN_CAPTURE_REQUEST_CODE)
- }
- binding.btn6.setOnClickListener {
- stopRecording()
- }
-
- binding.btnScreenshot.setOnClickListener {
-
- startActivityForResult( mediaProjectionManager.createScreenCaptureIntent(), REQUEST_SCREENSHOT)
- }
- }
-
- override fun onInitPadding(): Boolean = true
-
-
- private fun initPermissionLauncher() {
- requestPermissionLauncher =
- registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
- if (isGranted) {
- startForegroundService()
- Common.showLog("权限授予")
- } else {
- Common.showLog("权限拒绝")
- }
- }
-
- micPermissionLauncher = registerForActivityResult(
- ActivityResultContracts.RequestPermission()
- ) { isGranted ->
- if (isGranted) {
- Common.showLog("mic 权限授予")
- } else {
- Common.showLog("mic 权限拒绝")
- }
- }
- cameraPermissionLauncher = registerForActivityResult(
- ActivityResultContracts.RequestPermission()
- ) { isGranted ->
- if (isGranted) {
- Common.showLog("CAMERA 权限授予")
- } else {
- Common.showLog("CAMERA 权限拒绝")
- }
- }
- requestStoragePermission = registerForActivityResult(
- ActivityResultContracts.RequestPermission()
- ) { isGranted ->
- if (isGranted) {
- // 权限已授予
- Common.showLog("已获取存储权限")
- // 执行写入文件操作
- } else {
- Common.showLog( "存储权限被拒绝" )
- }
- }
-
-
- }
- fun checkStoragePermissionAndDoSomething() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
- ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED
- ) {
- requestStoragePermission.launch(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
- } else {
- // Android 10+ 不需要写权限,或者权限已获取
- // 执行写入文件操作
- Common.showLog("已获取存储权限")
- }
- }
-
- private fun checkSyswindow(context: Context) {
- if (!Settings.canDrawOverlays(context)) {
- val intent = Intent(
- Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
- Uri.parse("package:${context.packageName}")
- )
- startActivity(intent)
- }
-
- }
-
-
- private fun requestNotification() {
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- if (ContextCompat.checkSelfPermission(
- this,
- android.Manifest.permission.POST_NOTIFICATIONS
- )
- != PackageManager.PERMISSION_GRANTED
- ) {
- requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
- } else {
- // 权限已授予,可以发送通知
- Common.showLog("权限已授予,可以发送通知")
- startForegroundService()
- }
- } else {
- // Android 12 及以下,不需要请求权限
- Common.showLog("不需要请求权限")
- startForegroundService()
- }
- }
-
-
- private fun checkCamera() {
- if (ContextCompat.checkSelfPermission(
- this,
- android.Manifest.permission.CAMERA
- )
- != PackageManager.PERMISSION_GRANTED
- ) {
- cameraPermissionLauncher.launch(android.Manifest.permission.CAMERA)
- } else {
- Common.showLog("权限已授予 CAMERA")
-
- }
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- val mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!)
- if (requestCode == SCREEN_CAPTURE_REQUEST_CODE && resultCode == RESULT_OK) {
-
- startRecording(mediaProjection)
- }else if (requestCode == REQUEST_SCREENSHOT && resultCode == RESULT_OK && data != null) {
-// ScreenCaptureHelper.startScreenCapture(this,mediaProjection)
- }
- }
-
- private fun startForegroundService() {
-// FloatingWindowBridge.startAndBindService(this)
- }
-
- fun startRecording(mediaProjection: MediaProjection) {
- val screen = VideoFileHelper.getScreenInfo(this@MainActivity)
- val width = VideoFileHelper.alignTo16(screen.width)
- val height = VideoFileHelper.alignTo16(screen.height)
- initRecorder(width, height)
- mediaRecorder.start()
-
- val virtualDisplay = mediaProjection.createVirtualDisplay(
- "ScreenRecord",
- width, height, resources.displayMetrics.densityDpi,
- DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
- mediaRecorder.surface, null, null
- )
- }
-
- fun stopRecording() {
- Common.showLog("-------录屏完成.....")
- mediaRecorder.stop()
- mediaRecorder.reset()
- }
-
-
- private lateinit var mediaRecorder: MediaRecorder
-
- fun initRecorder(width: Int, height: Int) {
-
- val (videoUri, pfd) = VideoFileHelper.createVideoFile(this, Common.folderName)
- tmpVideoUri = videoUri
-
- mediaRecorder = MediaRecorder().apply {
- setAudioSource(MediaRecorder.AudioSource.MIC)
- setVideoSource(MediaRecorder.VideoSource.SURFACE)
- setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
- setOutputFile(pfd?.fileDescriptor)
- setVideoSize(width, height)
- setVideoEncoder(MediaRecorder.VideoEncoder.H264)
- setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
- setVideoEncodingBitRate(8 * 1000 * 1000)
- setVideoFrameRate(30)
- prepare()
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
-
- }
-
-
-
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/activity/MainActivity1.kt b/app/src/main/java/com/audio/record/screen/test/activity/MainActivity1.kt
index e5fd3dd..e7580ea 100644
--- a/app/src/main/java/com/audio/record/screen/test/activity/MainActivity1.kt
+++ b/app/src/main/java/com/audio/record/screen/test/activity/MainActivity1.kt
@@ -1,6 +1,5 @@
package com.audio.record.screen.test.activity
-import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -13,24 +12,18 @@ import android.widget.ImageView
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
-import androidx.core.view.marginBottom
-import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.audio.record.screen.test.R
import com.audio.record.screen.test.adapter.ViewPager2Adapter
import com.audio.record.screen.test.base.BaseActivity
import com.audio.record.screen.test.databinding.ActivityMain1Binding
import com.audio.record.screen.test.dialog.DialogPermission
-import com.audio.record.screen.test.fragment.MainFragment
import com.audio.record.screen.test.service.ConnectionListener
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.Extend.setMarginBottom
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 com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.google.android.material.tabs.TabLayoutMediator
@@ -64,6 +57,8 @@ class MainActivity1 : BaseActivity(), ConnectionListener,F
initLauncher()
firstCheck()
checkStoragePermissionAndDoSomething()
+
+
}
private fun initLauncher() {
@@ -73,6 +68,7 @@ class MainActivity1 : BaseActivity(), ConnectionListener,F
if (isGranted) {
// 权限已授予
Common.showLog("已获取存储权限")
+
showPermissionDialog()
// 执行写入文件操作
} else {
@@ -83,14 +79,21 @@ class MainActivity1 : BaseActivity(), ConnectionListener,F
requestNotificationLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
- startForegroundService()
Common.showLog("权限授予")
} else {
Common.showLog("权限拒绝")
}
}
-
+ screenCaptureLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) { result ->
+ if (result.resultCode == RESULT_OK && result.data != null) {
+ val data: Intent? = result.data
+ FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
+ startForegroundService()
+ }
+ }
// screenCaptureLauncher = registerForActivityResult(
// ActivityResultContracts.StartActivityForResult()
// ) { result ->
@@ -161,9 +164,10 @@ class MainActivity1 : BaseActivity(), ConnectionListener,F
private fun showPermissionDialog() {
if (isNotification && isOverlay) {
+ requestScreenPermission()
return
}
- mPermissionDialog = mPermissionDialog ?: DialogPermission {
+ mPermissionDialog = mPermissionDialog ?: DialogPermission ({
when (it) {
DialogPermission.type_ball -> {
intentSysWindow(this@MainActivity1)
@@ -173,11 +177,19 @@ class MainActivity1 : BaseActivity(), ConnectionListener,F
requestNotificationLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
}
}
+ }){
+ //消失
+ requestScreenPermission()
}
+
mPermissionDialog?.show(supportFragmentManager, "")
}
-
+ private fun requestScreenPermission() {
+ val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
+ val intent = mediaProjectionManager.createScreenCaptureIntent()
+ screenCaptureLauncher.launch(intent)
+ }
//悬浮窗
private fun intentSysWindow(context: Context) {
val intent = Intent(
@@ -191,9 +203,7 @@ class MainActivity1 : BaseActivity(), ConnectionListener,F
private fun firstCheck() {
Permission.checkNotification(this@MainActivity1) {
isNotification = it
- if (it) {
- startForegroundService()
- }
+
}
Permission.checkOvalApp(this@MainActivity1) {
isOverlay = it
diff --git a/app/src/main/java/com/audio/record/screen/test/activity/PreviewActivity.kt b/app/src/main/java/com/audio/record/screen/test/activity/PreviewActivity.kt
deleted file mode 100644
index 61c6ebf..0000000
--- a/app/src/main/java/com/audio/record/screen/test/activity/PreviewActivity.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-package com.audio.record.screen.test.activity
-
-
-import android.graphics.RectF
-import android.net.Uri
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.lifecycle.ViewModelProvider
-import androidx.media3.common.MediaItem
-import androidx.media3.common.PlaybackParameters
-import androidx.media3.exoplayer.ExoPlayer
-import androidx.navigation.fragment.NavHostFragment
-import com.audio.record.screen.test.App
-import com.audio.record.screen.test.R
-import com.audio.record.screen.test.base.BaseActivity
-import com.audio.record.screen.test.databinding.ActivityPreviewBinding
-import com.audio.record.screen.test.tool.Common
-import com.audio.record.screen.test.tool.FFmpegKitTool
-import com.audio.record.screen.test.viewmodel.PreviewViewModel
-import java.io.File
-import kotlin.properties.Delegates
-
-
-class PreviewActivity : BaseActivity(), View.OnClickListener {
-
- private lateinit var exoPlayer: ExoPlayer
-
- //将当前需要处理的原视频复制到内部存储,方便操作
- private lateinit var copyFile: File
-
- private var copyResult by Delegates.notNull()
- private lateinit var viewModel: PreviewViewModel
- override fun initBinding(): ActivityPreviewBinding =
- ActivityPreviewBinding.inflate(layoutInflater)
-
-
- override fun getFullColor(): Boolean = true
- override fun onInitPadding(): Boolean = true
- override fun onCreateInit() {
- initPlay()
- viewModel = ViewModelProvider(this)[PreviewViewModel::class.java]
- binding.imPlay.setOnClickListener {
- if (!binding.imPlay.isSelected) {
- exoPlayer.play()
- } else {
- exoPlayer.pause()
- }
- binding.imPlay.isSelected = !binding.imPlay.isSelected
- }
-
- val thumbDir = File(App.instanceApp.cacheDir, "thumb").apply {
- if (!exists()) {
- mkdir()
- } else {
- Common.deleteAllFilesInDirectory(this.absolutePath)
- }
- }
-// val thumbAdapter = ThumbAdapter(this@PreviewActivity)
-// binding.thumbRecycler.apply {
-// layoutManager =
-// LinearLayoutManager(this@PreviewActivity, RecyclerView.HORIZONTAL, false)
-// adapter = thumbAdapter
-// }
- val open = App.instanceApp.assets.open("record_1748398994963.mp4")
- copyFile = File(App.instanceApp.cacheDir, "temp_video.mp4")
- //原视频复制到内部存储
- FFmpegKitTool.copy(copyFile, open, thumbDir.absolutePath) {
- Common.showLog("--------copy success")
- copyResult = it
- }
-
-
- initListener()
- viewModel.cropRatioText.observe(this) {
- if (it.equals("原始")) {
- val videoRatio = Common.getVideoRatio(copyFile.absolutePath)
- binding.cropView.setAspectRatio(videoRatio)
- } else {
- val split = it.split(":")
- binding.cropView.setAspectRatio(split[0].toFloat() / split[1].toFloat())
- }
- }
- viewModel.saveCrop.observe(this){
-
- val cropFile = File(App.instanceApp.cacheDir, "crop_video_${System.currentTimeMillis()}.mp4")
- val videoWH = Common.getVideoWH(copyFile.absolutePath)
-
- val rawCropRect = binding.cropView.getCropRectInVideoCoords(videoWH.first ,
- videoWH.second
- )
-
- val x = rawCropRect?.left?.toInt() ?: 0
- val y = rawCropRect?.top?.toInt() ?: 0
- val w = rawCropRect?.width()?.toInt() ?: 0
- val h = rawCropRect?.height()?.toInt() ?: 0
-
- Common.showLog("-------videoWH w=${videoWH.first} h=${videoWH.second} x=${x} y = $y w= $w h= $h")
- FFmpegKitTool.cropVideo(copyFile.absolutePath,cropFile.absolutePath,x,y,w,h)
-
-
- }
- viewModel.changeSpeed.observe(this){
- exoPlayer.playbackParameters = PlaybackParameters(it, it)
- }
-
- }
-
- private fun initListener() {
- val navHostFragment = supportFragmentManager
- .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
- val navController = navHostFragment.navController
- navController.addOnDestinationChangedListener { controller, destination, arguments ->
- Common.showLog("NavControllerListener 当前的目的地: id=${destination.id} name=${destination.displayName} label=${destination.label}")
- binding.cropView.isVisible = false
- when (destination.id) {
- R.id.fragmentCut -> {
- viewModel.updateCopyResult(Pair(copyResult, copyFile.absolutePath))
- Common.showLog("----fragmentCut")
- }
-
- R.id.fragmentCropping -> {
- Common.showLog("----fragmentCropping")
- initCropView()
-
- }
-
- R.id.fragmentVolume -> {
- Common.showLog("----fragmentVolume")
- viewModel.updateCopyResult(Pair(copyResult, copyFile.absolutePath))
- }
-
- R.id.fragmentSpeed -> {
- Common.showLog("----fragmentSpeed")
- viewModel.updateCopyResult(Pair(copyResult, copyFile.absolutePath))
- }
- }
- }
- }
-
-
- private fun initCropView() {
- binding.cropView.isVisible = true
- // 延迟执行以确保布局完成
- binding.playerView.post {
- val contentFrame: View =
- binding.playerView.findViewById(androidx.media3.ui.R.id.exo_content_frame)
- if (contentFrame != null) {
- // 获取在 cropView 坐标系中的显示区域
- val videoRect = RectF()
- val contentLoc = IntArray(2)
- val cropLoc = IntArray(2)
- contentFrame.getLocationOnScreen(contentLoc)
- binding.cropView.getLocationOnScreen(cropLoc)
- val offsetX = (contentLoc[0] - cropLoc[0]).toFloat()
- val offsetY = (contentLoc[1] - cropLoc[1]).toFloat()
- videoRect.left = offsetX
- videoRect.top = offsetY
- videoRect.right = offsetX + contentFrame.width
- videoRect.bottom = offsetY + contentFrame.height
- // 设置裁剪区域限制边界
- binding.cropView.setVideoDisplayBounds(videoRect)
- }
- }
- }
-
-
- private fun initPlay() {
- val uri = Uri.parse("asset:///record_1748398994963.mp4")
- exoPlayer = ExoPlayer.Builder(this).build()
-
- binding.playerView.player = exoPlayer
- val mediaItem = MediaItem.fromUri(uri)
- exoPlayer.setMediaItem(mediaItem)
- exoPlayer.prepare()
-// exoPlayer.playWhenReady = true
- }
-
- override fun onClick(p0: View?) {
-
- }
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/activity/ScreenPermissionActivity.kt b/app/src/main/java/com/audio/record/screen/test/activity/ScreenPermissionActivity.kt
index 053493c..ba65be7 100644
--- a/app/src/main/java/com/audio/record/screen/test/activity/ScreenPermissionActivity.kt
+++ b/app/src/main/java/com/audio/record/screen/test/activity/ScreenPermissionActivity.kt
@@ -2,12 +2,18 @@ package com.audio.record.screen.test.activity
import android.content.Context
import android.content.Intent
+import android.graphics.Color
import android.media.projection.MediaProjectionManager
+import android.os.Build
import android.os.Bundle
import android.os.PersistableBundle
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowInsetsController
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.WindowCompat
import com.audio.record.screen.test.base.BaseActivity
import com.audio.record.screen.test.databinding.ActivityPlayBinding
import com.audio.record.screen.test.service.FloatingWindowBridge
@@ -29,14 +35,46 @@ class ScreenPermissionActivity : AppCompatActivity() {
}
private var withAudio = false
+ private lateinit var mediaProjectionManager:MediaProjectionManager
+ override fun onWindowFocusChanged(hasFocus: Boolean) {
+ super.onWindowFocusChanged(hasFocus)
+ if (hasFocus) {
+// Common.hideSystemBars(this)
+// Common.setStatusBarTextColor(this, false)
+ setFullScreenTransparent()
+ }
+ }
+ private fun setFullScreenTransparent() {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ window.insetsController?.let {
+ it.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
+ it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ }
+ } else {
+ @Suppress("DEPRECATION")
+ window.decorView.systemUiVisibility =
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ }
+
+ window.statusBarColor = Color.TRANSPARENT
+ window.navigationBarColor = Color.TRANSPARENT
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- Common.setStatusBarTextColor(this, true)
+// Common.setStatusBarTextColor(this, true)
+ mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
withAudio = intent.getBooleanExtra(key_with_audio, false)
screenCaptureLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
+ Common.showLog("--------------screenCaptureLauncher")
if (result.resultCode == RESULT_OK && result.data != null) {
val data: Intent? = result.data
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
@@ -63,7 +101,7 @@ class ScreenPermissionActivity : AppCompatActivity() {
private fun requestScreenPermission() {
- val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
+ Common.showLog("--------------requestScreenPermission")
val intent = mediaProjectionManager.createScreenCaptureIntent()
screenCaptureLauncher.launch(intent)
}
diff --git a/app/src/main/java/com/audio/record/screen/test/dialog/DialogPermission.kt b/app/src/main/java/com/audio/record/screen/test/dialog/DialogPermission.kt
index d8f230c..4e4784c 100644
--- a/app/src/main/java/com/audio/record/screen/test/dialog/DialogPermission.kt
+++ b/app/src/main/java/com/audio/record/screen/test/dialog/DialogPermission.kt
@@ -1,5 +1,6 @@
package com.audio.record.screen.test.dialog
+import android.content.DialogInterface
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
@@ -11,7 +12,7 @@ import com.audio.record.screen.test.databinding.DialogPermissionBinding
import com.audio.record.screen.test.tool.Permission
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-class DialogPermission(private var mClickType: (type: Int) -> Unit) : BottomSheetDialogFragment() {
+class DialogPermission(private var mClickType: (type: Int) -> Unit,private var onDismiss:()->Unit) : BottomSheetDialogFragment() {
private lateinit var vb: DialogPermissionBinding
override fun onCreateView(
inflater: LayoutInflater,
@@ -28,6 +29,7 @@ class DialogPermission(private var mClickType: (type: Int) -> Unit) : BottomShee
val dialog = dialog
if (dialog != null) {
dialog.setCanceledOnTouchOutside(false)
+ dialog.setCancelable(false)
val window = dialog.window
if (window != null) {
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
@@ -39,6 +41,11 @@ class DialogPermission(private var mClickType: (type: Int) -> Unit) : BottomShee
}
}
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ onDismiss.invoke()
+ }
+
private fun init() {
vb.run {
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/CropFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/CropFragment.kt
deleted file mode 100644
index 43bb318..0000000
--- a/app/src/main/java/com/audio/record/screen/test/fragment/CropFragment.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.audio.record.screen.test.fragment
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.lifecycle.ViewModelProvider
-import androidx.navigation.NavController
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.audio.record.screen.test.R
-import com.audio.record.screen.test.adapter.CropAdapter
-import com.audio.record.screen.test.base.BaseFragment
-import com.audio.record.screen.test.databinding.FragmentCropBinding
-import com.audio.record.screen.test.viewmodel.PreviewViewModel
-
-class CropFragment : BaseFragment(), View.OnClickListener {
-
-
- private lateinit var navController: NavController
- private lateinit var viewModel: PreviewViewModel
-
- companion object {
-
- @JvmStatic
- fun newInstance() =
- CropFragment().apply {
- arguments = Bundle().apply {
-
- }
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- arguments?.let {
-
- }
- }
-
-
- override fun initBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCropBinding =
- FragmentCropBinding.inflate(inflater, container, false)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- navController = findNavController()
-
- viewModel = ViewModelProvider(requireActivity())[PreviewViewModel::class.java]
- binding.recycler.run {
- val stringList = resources.getStringArray(R.array.crop_text).toList()
- adapter = CropAdapter(requireContext()) {
- viewModel.updateCropText(it)
-
- }.apply { updateData(stringList) }
- layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
- }
- initClick()
- }
-
- private fun initClick() {
- binding.imClose.setOnClickListener(this)
- binding.imSave.setOnClickListener(this)
- }
-
- override fun onClick(v: View?) {
- v?.let {
- if (it == binding.imSave) {
- viewModel.updateClickCropSave(true)
- } else if (it == binding.imClose) {
- navController.navigateUp()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/CutFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/CutFragment.kt
deleted file mode 100644
index 6287b85..0000000
--- a/app/src/main/java/com/audio/record/screen/test/fragment/CutFragment.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.audio.record.screen.test.fragment
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.lifecycle.ViewModelProvider
-import androidx.navigation.NavController
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.audio.record.screen.test.App
-import com.audio.record.screen.test.adapter.ThumbAdapter
-import com.audio.record.screen.test.base.BaseFragment
-import com.audio.record.screen.test.databinding.FragmentCutBinding
-import com.audio.record.screen.test.tool.Common
-import com.audio.record.screen.test.tool.FFmpegKitTool
-import com.audio.record.screen.test.viewmodel.PreviewViewModel
-import com.jaygoo.widget.OnRangeChangedListener
-import com.jaygoo.widget.RangeSeekBar
-import java.io.File
-import kotlin.properties.Delegates
-
-class CutFragment : BaseFragment() ,View.OnClickListener{
- //毫秒单位
- private var leftV by Delegates.notNull()
- private lateinit var viewModel: PreviewViewModel
- private var rightV by Delegates.notNull()
- private var param1: String? = null
- private var param2: String? = null
- private lateinit var copyFilePath: String
- private lateinit var navController:NavController
- companion object {
-
- @JvmStatic
- fun newInstance() =
- CutFragment().apply {
- arguments = Bundle().apply {
-// putString(ARG_PARAM1, param1)
-// putString(ARG_PARAM2, param2)
- }
- }
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- arguments?.let {
-// param1 = it.getString(ARG_PARAM1)
-// param2 = it.getString(ARG_PARAM2)
- }
- }
-
-
- override fun initBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCutBinding =
- FragmentCutBinding.inflate(inflater, container, false)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- navController = findNavController()
- viewModel = ViewModelProvider(requireActivity())[PreviewViewModel::class.java]
- initThumb()
- initClick()
- }
-
-
- private fun initThumb(){
- val thumbDir = File(App.instanceApp.cacheDir, "thumb")
- val thumbAdapter = ThumbAdapter(requireContext())
- binding.thumbRecycler.apply {
- layoutManager =
- LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
- adapter = thumbAdapter
- }
-// val open = App.instanceApp.assets.open("temp_video.mp4")
-// tempFile = File(App.instanceApp.cacheDir, "temp_video.mp4")
- viewModel.copySuccess.observe(requireActivity()){
- if (it.first) {
- Common.showLog("-------11111111111111")
- copyFilePath = it.second
- val allImagePaths = Common.getNaturallySortedThumbFiles(thumbDir.absolutePath)
- thumbAdapter.updateData(allImagePaths)
- initSeekBar()
- }
- }
-
- }
- private fun initClick() {
- binding.imClose.setOnClickListener(this)
- binding.imSave.setOnClickListener(this)
- }
- private fun initSeekBar() {
- val durationMs = Common.getVideoDurationMs(copyFilePath)
-
-// val millisToSeconds = Common.millisToSeconds(durationMs)
- Common.showLog("----durationMs=${durationMs} ")
- binding.rangeSlider.run {
- durationMs.toFloat().let {
- setRange(0f, it)
- leftSeekBar?.setIndicatorText(Common.formatSeconds(0f))
- rightSeekBar?.setIndicatorText(Common.formatSeconds(it))
- setProgress(0f, it)
-
- }
-
-
- setOnRangeChangedListener(object : OnRangeChangedListener {
- override fun onRangeChanged(
- view: RangeSeekBar?,
- leftValue: Float,
- rightValue: Float,
- isFromUser: Boolean
- ) {
- view?.leftSeekBar?.setIndicatorText(Common.formatSeconds(leftValue))
- view?.rightSeekBar?.setIndicatorText(Common.formatSeconds(rightValue))
- leftV = leftValue
- rightV = rightValue
-
- }
-
- override fun onStartTrackingTouch(view: RangeSeekBar?, isLeft: Boolean) {
-
- }
-
- override fun onStopTrackingTouch(view: RangeSeekBar?, isLeft: Boolean) {
-
- }
-
- })
- }
- }
-
- override fun onClick(p0: View?) {
- p0?.let {
- if (it == binding.imSave) {
- trimVideoFile()
- }else if(it == binding.imClose){
- navController.navigateUp()
- }
- }
- }
- fun trimVideoFile() {
- val resultFile = File(requireContext().cacheDir, "test_${System.currentTimeMillis()}.mp4")
- val left = leftV.toString()
- val right = rightV.toString()
- Common.showLog("------left=${left} right=${right}")
-// FFmpegKitTool.trimVideo(tempFile.absolutePath, resultFile.absolutePath, left, right)
- FFmpegKitTool.cropVideoWithFFmpeg(copyFilePath, resultFile.absolutePath,leftV,rightV)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/SpeedFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/SpeedFragment.kt
deleted file mode 100644
index 1584a9c..0000000
--- a/app/src/main/java/com/audio/record/screen/test/fragment/SpeedFragment.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.audio.record.screen.test.fragment
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.SeekBar
-import android.widget.SeekBar.OnSeekBarChangeListener
-import androidx.lifecycle.ViewModelProvider
-import androidx.navigation.NavController
-import androidx.navigation.fragment.findNavController
-import com.audio.record.screen.test.App
-import com.audio.record.screen.test.base.BaseFragment
-import com.audio.record.screen.test.databinding.FragmentSpeedBinding
-import com.audio.record.screen.test.tool.Common
-import com.audio.record.screen.test.tool.FFmpegKitTool
-import com.audio.record.screen.test.viewmodel.PreviewViewModel
-import java.io.File
-
-class SpeedFragment : BaseFragment(), View.OnClickListener {
-
-
- private lateinit var viewModel: PreviewViewModel
- private lateinit var navController: NavController
- private lateinit var copyFilePath: String
- private var speed = 1f
-
- companion object {
-
- @JvmStatic
- fun newInstance() =
- SpeedFragment().apply {
- arguments = Bundle().apply {
-// putString(ARG_PARAM1, param1)
-// putString(ARG_PARAM2, param2)
- }
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- arguments?.let {
-// param1 = it.getString(ARG_PARAM1)
-// param2 = it.getString(ARG_PARAM2)
- }
- }
-
-
- override fun initBinding(
- inflater: LayoutInflater,
- container: ViewGroup?
- ): FragmentSpeedBinding =
- FragmentSpeedBinding.inflate(inflater, container, false)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- viewModel = ViewModelProvider(requireActivity())[PreviewViewModel::class.java]
- navController = findNavController()
-
- initClick()
- viewModel.copySuccess.observe(requireActivity()){
- if (it.first) {
- Common.showLog("-------11111111111111")
- copyFilePath = it.second
-
- }
- }
- }
-
-
- private fun initClick() {
- binding.imClose.setOnClickListener(this)
- binding.imSave.setOnClickListener(this)
- binding.seekbar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
- override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
- speed = 0.5f + (progress / 150f) * 1.5f
- binding.progressText.text = "${speed}X"
- viewModel.updateSpeed(speed)
- Common.showLog("--------progress=${progress} speed=${speed}")
- }
-
- override fun onStartTrackingTouch(seekBar: SeekBar?) {
-
- }
-
- override fun onStopTrackingTouch(seekBar: SeekBar?) {
-
- }
-
- })
- }
-
-
- override fun onClick(p0: View?) {
- p0?.let {
- if (it == binding.imSave) {
- val volumeFile = File(App.instanceApp.cacheDir, "speed_video_${System.currentTimeMillis()}.mp4")
- FFmpegKitTool.buildSpeedCommand(copyFilePath,volumeFile.absolutePath,speed)
- } else if (it == binding.imClose) {
- navController.navigateUp()
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/VolumeFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/VolumeFragment.kt
deleted file mode 100644
index 818a040..0000000
--- a/app/src/main/java/com/audio/record/screen/test/fragment/VolumeFragment.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.audio.record.screen.test.fragment
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.SeekBar
-import android.widget.SeekBar.OnSeekBarChangeListener
-import androidx.lifecycle.ViewModelProvider
-import androidx.navigation.NavController
-import androidx.navigation.fragment.findNavController
-import com.audio.record.screen.test.App
-import com.audio.record.screen.test.base.BaseFragment
-import com.audio.record.screen.test.databinding.FragmentVolumeBinding
-import com.audio.record.screen.test.tool.Common
-import com.audio.record.screen.test.tool.FFmpegKitTool
-import com.audio.record.screen.test.viewmodel.PreviewViewModel
-import java.io.File
-
-class VolumeFragment : BaseFragment(), View.OnClickListener {
-
-
- private lateinit var viewModel: PreviewViewModel
- private lateinit var navController: NavController
- private lateinit var copyFilePath: String
- companion object {
-
- @JvmStatic
- fun newInstance() =
- VolumeFragment().apply {
- arguments = Bundle().apply {
-// putString(ARG_PARAM1, param1)
-// putString(ARG_PARAM2, param2)
- }
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- arguments?.let {
-// param1 = it.getString(ARG_PARAM1)
-// param2 = it.getString(ARG_PARAM2)
- }
- }
-
-
- override fun initBinding(
- inflater: LayoutInflater,
- container: ViewGroup?
- ): FragmentVolumeBinding =
- FragmentVolumeBinding.inflate(inflater, container, false)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- viewModel = ViewModelProvider(requireActivity())[PreviewViewModel::class.java]
- navController = findNavController()
-
- initClick()
- viewModel.copySuccess.observe(requireActivity()){
- if (it.first) {
- Common.showLog("-------11111111111111")
- copyFilePath = it.second
-
- }
- }
- }
-
-
- private fun initClick() {
- binding.imClose.setOnClickListener(this)
- binding.imSave.setOnClickListener(this)
- binding.seekbar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
- override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
- binding.progressText.text = "${progress}%"
- }
-
- override fun onStartTrackingTouch(seekBar: SeekBar?) {
-
- }
-
- override fun onStopTrackingTouch(seekBar: SeekBar?) {
-
- }
-
- })
- }
-
-
- override fun onClick(p0: View?) {
- p0?.let {
- if (it == binding.imSave) {
- val volumeFile = File(App.instanceApp.cacheDir, "volume_video_${System.currentTimeMillis()}.mp4")
- val volume = (binding.seekbar.progress.coerceIn(0, 200)) / 100.0f
- FFmpegKitTool.setVideVolume(copyFilePath,volumeFile.absolutePath,volume)
-
- } else if (it == binding.imClose) {
- navController.navigateUp()
- }
- }
- }
-
-}
\ No newline at end of file
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 3e18810..740c188 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
@@ -197,10 +197,12 @@ class RecordNormalFragment : BaseFragment(), Floati
}
private fun setScreenshot(boolean: Boolean) {
+ Common.showLog("----------setScreenshot=${boolean}")
if (boolean) {
- requestRecordPermission(ConstValue.type_show_screenshot){
- FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
- }
+// requestRecordPermission(ConstValue.type_show_screenshot){
+//// FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
+// }
+ FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
} else {
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_hide_screenshot)
@@ -217,7 +219,7 @@ class RecordNormalFragment : BaseFragment(), Floati
private fun startRecorder() {
requestRecordPermission( ConstValue.type_record){
- startCountDown()
+// startCountDown()
}
}
@@ -227,13 +229,9 @@ class RecordNormalFragment : BaseFragment(), Floati
*/
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()
- }
+ val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
+ pendingType = type
+ recorderLauncher.launch(captureIntent)
}
diff --git a/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordingFragment.kt b/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordingFragment.kt
index 80f1ff7..1da0bb8 100644
--- a/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordingFragment.kt
+++ b/app/src/main/java/com/audio/record/screen/test/fragment/child/RecordingFragment.kt
@@ -108,6 +108,7 @@ class RecordingFragment : BaseFragment(), FloatingCall
override fun onUpdateRecordTime(time: String) {
super.onUpdateRecordTime(time)
+ Common.showLog("=======onUpdateRecordTime 时间更新")
binding.tvTimer.text = time
}
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 16d2af2..17cd972 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
@@ -146,7 +146,7 @@ class ScreenRecordService : Service() {
private val timeUpdateRunnable = object : Runnable {
override fun run() {
-
+ Common.showLog("=======timeUpdateRunnable 时间更新 isPause=${isPause}")
val currentRecordingTimeInMillis =
System.currentTimeMillis() - recordingStartTime - totalPausedTime
val elapsed = currentRecordingTimeInMillis / 1000
@@ -156,8 +156,10 @@ class ScreenRecordService : Service() {
callbacks.forEach {
it.get()?.onUpdateRecordTime(onRecordingTimeChanged)
}
- if (!isPause)
+ if (!isPause){
mRecorderHandler.postDelayed(this, updateInterval)
+ }
+
}
}
@@ -277,8 +279,9 @@ class ScreenRecordService : Service() {
*/
private fun startScreenshot(showView: Boolean) {
// TODO: 开始截屏
- mIntent?.let { intent ->
- mediaProjectionManager.getMediaProjection(mCode, intent)?.let { mediaProjection ->
+
+ createMediaProject {
+ it?.let { mediaProjection->
hideScreenshot()
val intent = Intent(this, ScreenshotAnimActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -291,8 +294,10 @@ class ScreenRecordService : Service() {
if (showView)
showScreenshot()
}
+
}
}
+
}
@SuppressLint("ClickableViewAccessibility")
@@ -384,7 +389,7 @@ class ScreenRecordService : Service() {
}
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
}
- if (!isBallViewAdded&&!isBallExpandViewAdded) {
+ if (!isBallViewAdded && !isBallExpandViewAdded) {
windowManager.addView(ballView, layoutParamsBall)
isBallViewAdded = true
}
@@ -482,6 +487,7 @@ class ScreenRecordService : Service() {
}
layoutParamsBallExpand.y = aY
+ updateBall()
windowManager.addView(ballViewExpand, layoutParamsBallExpand)
isBallExpandViewAdded = true
delayRemove {
@@ -529,7 +535,8 @@ class ScreenRecordService : Service() {
windowManager,
true
) {
- startScreenshot(true)
+ ballType = ConstValue.type_screenshot
+ intentPermission(false)
}
}
if (!isScreenshotViewAdded) {
@@ -662,23 +669,27 @@ class ScreenRecordService : Service() {
//倒计时结束后,开启录制
Common.showLog("Service--- 倒计时结束")
hideCountDown()
- mIntent?.let { intent ->
- mediaProjectionManager.getMediaProjection(mCode, intent)
- ?.let { media ->
- mediaProjection = media
- startRecording()
- callbacks.forEach {
- it.get()?.onStartRecording()
- }
- }
+ startRecording()
+ callbacks.forEach {
+ it.get()?.onStartRecording()
}
-
}
}
}
+
+ private fun createMediaProject(onAction: (med: MediaProjection?) -> Unit) {
+ mIntent?.let { intent ->
+ mediaProjectionManager.getMediaProjection(mCode, intent)
+ ?.let { media ->
+ mediaProjection = media
+ onAction.invoke(media)
+ } ?: onAction.invoke(null)
+ } ?: onAction.invoke(null)
+ }
+
private fun animateNext(
countdownValues: Array,
tvCountdown: TextView,
@@ -741,37 +752,40 @@ class ScreenRecordService : Service() {
totalPausedTime = 0L
mRecorderHandler.post(timeUpdateRunnable)
- mediaProjection?.let {
- it.registerCallback(object : MediaProjection.Callback() {
- override fun onStop() {
- Common.showLog("MediaProjection 被系统或用户停止")
- // 这里应该释放 MediaRecorder 和 VirtualDisplay 等
- stopRecording()
+ createMediaProject{ curmed->
+ mediaProjection?.let {
+ it.registerCallback(object : MediaProjection.Callback() {
+ override fun onStop() {
+ Common.showLog("MediaProjection 被系统或用户停止")
+ // 这里应该释放 MediaRecorder 和 VirtualDisplay 等
+ stopRecording()
- }
- }, Handler(Looper.getMainLooper()))
- virtualDisplay = it.createVirtualDisplay(
- "ScreenRecord",
- width, height, resources.displayMetrics.densityDpi,
- DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
- mediaRecorder.surface, object : VirtualDisplay.Callback() {
- override fun onPaused() {
- super.onPaused()
- Common.showLog("--VirtualDisplay.Callback..onPaused...")
}
+ }, Handler(Looper.getMainLooper()))
+ virtualDisplay = it.createVirtualDisplay(
+ "ScreenRecord",
+ width, height, resources.displayMetrics.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ mediaRecorder.surface, object : VirtualDisplay.Callback() {
+ override fun onPaused() {
+ super.onPaused()
+ Common.showLog("--VirtualDisplay.Callback..onPaused...")
+ }
- override fun onResumed() {
- super.onResumed()
- Common.showLog("--VirtualDisplay.Callback..onResumed...")
- }
+ override fun onResumed() {
+ super.onResumed()
+ Common.showLog("--VirtualDisplay.Callback..onResumed...")
+ }
- override fun onStopped() {
- super.onStopped()
- Common.showLog("--VirtualDisplay.Callback..onStopped...")
- }
- }, null
- )
+ override fun onStopped() {
+ super.onStopped()
+ Common.showLog("--VirtualDisplay.Callback..onStopped...")
+ }
+ }, null
+ )
+ }
}
+
}
@@ -922,7 +936,11 @@ class ScreenRecordService : Service() {
fun getViewStatus() {
callbacks.forEach {
- it.get()?.onRefreshViewShow(isWebcamViewAdded, isScreenshotViewAdded, isBallViewAdded||isBallExpandViewAdded)
+ it.get()?.onRefreshViewShow(
+ isWebcamViewAdded,
+ isScreenshotViewAdded,
+ isBallViewAdded || isBallExpandViewAdded
+ )
}
}
diff --git a/app/src/main/java/com/audio/record/screen/test/tool/Common.kt b/app/src/main/java/com/audio/record/screen/test/tool/Common.kt
index e39b3fd..c228636 100644
--- a/app/src/main/java/com/audio/record/screen/test/tool/Common.kt
+++ b/app/src/main/java/com/audio/record/screen/test/tool/Common.kt
@@ -1,5 +1,6 @@
package com.audio.record.screen.test.tool
+import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
@@ -17,6 +18,8 @@ import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.View
import android.view.ViewConfiguration
+import android.view.WindowInsets
+import android.view.WindowInsetsController
import android.view.WindowManager
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
@@ -40,26 +43,6 @@ object Common {
}
fun setStatusBarTextColor(activity: Activity, dark: Boolean) {
-// val window = activity.window
-// val decor = window.decorView
-//
-// // 设置状态栏图标颜色(深色图标表示浅色背景)
-// var flags = decor.systemUiVisibility
-// flags = if (dark) {
-// flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
-// } else {
-// flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
-// }
-// // 保持布局全屏
-// flags = flags or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-// decor.systemUiVisibility = flags
-//
-// // 去除 TRANSLUCENT_STATUS,使用透明背景更现代
-// window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
-// window.statusBarColor = Color.TRANSPARENT
-
-
-
val window = activity.window
val decor = window.decorView
@@ -428,4 +411,30 @@ object Common {
}
+
+ @SuppressLint("InlinedApi")
+ fun hideSystemBars(activity: Activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // Android 11+
+ activity.window.setDecorFitsSystemWindows(false)
+ val controller = activity.window.insetsController
+ controller?.let {
+ it.hide(WindowInsets.Type.navigationBars() or WindowInsets.Type.statusBars())
+ it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ }
+ } else {
+ // Android 7 - 10
+ @Suppress("DEPRECATION")
+ activity.window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_FULLSCREEN
+ )
+ }
+ }
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/audio/record/screen/test/tool/FFmpegKitTool.kt b/app/src/main/java/com/audio/record/screen/test/tool/FFmpegKitTool.kt
deleted file mode 100644
index d8bfb10..0000000
--- a/app/src/main/java/com/audio/record/screen/test/tool/FFmpegKitTool.kt
+++ /dev/null
@@ -1,224 +0,0 @@
-package com.audio.record.screen.test.tool
-
-import android.media.MediaMetadataRetriever
-import android.os.Handler
-import android.os.Looper
-import android.util.Log
-import com.arthenica.ffmpegkit.FFmpegKit
-import com.arthenica.ffmpegkit.ReturnCode
-import com.audio.record.screen.test.App
-import java.io.File
-import java.io.FileOutputStream
-import java.io.InputStream
-import java.util.concurrent.TimeUnit
-
-object FFmpegKitTool {
-
- val mainHandler = Handler(Looper.getMainLooper())
-
- fun copy(
- tempFile: File,
- inputStream: InputStream,
- thumbDir: String,
- result: (ok: Boolean) -> Unit
- ) {
-
- FileOutputStream(tempFile).use { outputStream ->
- inputStream.copyTo(outputStream)
- outputStream.flush()
- extractThumbnailsFFmpeg(tempFile.absolutePath, thumbDir, result)
- }
-
- }
-
- fun createThumb(inputPath: String, outputDir: String, result: (ok: Boolean) -> Unit) {
-// val inputPath = "/storage/emulated/0/Movies/sample.mp4"
-// val outputDir = "/storage/emulated/0/Movies/thumbs" // 请确保已创建
- val cmd = "-i $inputPath -vf fps=10 $outputDir/thumb_%04d.jpg"
-
- FFmpegKit.executeAsync(cmd) { session ->
- val returnCode = session.returnCode
- if (ReturnCode.isSuccess(returnCode)) {
- Common.showLog("FFmpegKit 缩略图生成成功 ${Thread.currentThread().name}")
- result.invoke(true)
- } else {
- result.invoke(false)
- Common.showLog("FFmpegKit 缩略图生成失败:${session.failStackTrace}")
- }
- }
-
- }
-
-
- /**
- * 截取视频缩略图,均分6张图
- */
- fun extractThumbnailsFFmpeg(
- videoPath: String,
- outputDir: String,
- result: (ok: Boolean) -> Unit
- ) {
- var count = 0
- val durationMs = Common.getVideoDurationMs(videoPath)
- val durationSec = durationMs / 1000.0
- val step = durationSec / 7 // 均分6帧,跳过首尾
- Common.showLog("durationSec $durationSec step $step")
- for (i in 1..6) {
- val timestamp = i * step
- val outputPath = "$outputDir/thumb_$i.jpg"
- Common.showLog("i = $i timestamp $timestamp ")
- val cmd = "-ss $timestamp -i \"$videoPath\" -frames:v 1 -q:v 2 \"$outputPath\""
- FFmpegKit.executeAsync(cmd) { session ->
- val returnCode = session.returnCode
- count++
- if (count == 6) {
- mainHandler.post {
- if (returnCode.isValueSuccess) {
- result.invoke(true)
- Common.showLog("FFmpeg Frame $i saved to $outputPath")
- } else {
- result.invoke(false)
- Common.showLog("FFmpeg Failed to extract frame $i")
- }
-
- }
- }
-
-
- }
- }
- }
-
-
- /**
- * 剪切视频时长
- * @param startTime
- */
- fun cropVideoWithFFmpeg(
- inputPath: String,
- outputPath: String,
- startMs: Float,
- endMs: Float
- ) {
- val durationMs = endMs - startMs
- if (durationMs <= 0) {
- println("结束时间必须大于开始时间")
- return
- }
- val formatFloatTime = formatFloatTime(durationMs)
- val startTime = formatFloatTime(startMs)
- val endTime = formatFloatTime(endMs)
- val command =
- "-ss $startTime -i \"$inputPath\" -to $formatFloatTime -c:v copy -c:a copy \"$outputPath\""
- Common.showLog("--command=${command}")
-
- FFmpegKit.executeAsync(command) { session ->
- val returnCode = session.returnCode
- if (returnCode.isValueSuccess) {
- Common.showLog("裁剪成功: $outputPath")
- } else {
- Common.showLog("裁剪失败: ${session.failStackTrace}")
- }
- }
- }
-
- // 毫秒转为 ffmpeg 时间格式:hh:mm:ss.S
- fun formatFloatTime(ms: Float): String {
- val totalSeconds = ms / 1000f
- val hours = (totalSeconds / 3600).toInt()
- val minutes = ((totalSeconds % 3600) / 60).toInt()
- val seconds = (totalSeconds % 60).toInt()
- val tenths = ((totalSeconds % 1) * 10).toInt() // 保留一位小数
-
- return String.format("%02d:%02d:%02d.%01d", hours, minutes, seconds, tenths)
- }
-//
-// fun trimVideo(inputPath: String, outputPath: String, startTime: String, endTime: String) {
-// val command = "-ss $startTime -i $inputPath -to $endTime -c copy $outputPath"
-//
-// Common.showLog("--command=${command}")
-//
-// FFmpegKit.executeAsync(command) { session ->
-// when {
-// ReturnCode.isSuccess(session.returnCode) -> {
-// Log.d("FFmpegKit", "剪切成功!输出路径: $outputPath")
-// }
-//
-// session.returnCode.value == ReturnCode.CANCEL -> {
-// Log.w("FFmpegKit", "用户取消操作")
-// }
-//
-// else -> {
-// Log.e("FFmpegKit", "剪切失败. 错误日志: ${session.output}")
-// }
-// }
-// }
-// }
-
-
- /**
- * 裁切视频显示内容尺寸
- */
- fun cropVideo(
- inputPath: String,
- outputPath: String,
- cropX: Int,
- cropY: Int,
- cropWidth: Int,
- cropHeight: Int
- ) {
-// val cmd = "-i $inputPath -vf crop=$cropWidth:$cropHeight:$cropX:$cropY -c:a copy $outputPath"
- val cmd =
- "-i $inputPath -vf crop=$cropWidth:$cropHeight:$cropX:$cropY -c:v mpeg4 -qscale:v 2 -c:a copy $outputPath"
-
- Common.showLog("FFmpeg Crop 比例裁剪 $cmd")
- FFmpegKit.executeAsync(cmd) {
- if (ReturnCode.isSuccess(it.returnCode)) {
- Common.showLog("FFmpeg Crop 比例裁剪成功 $outputPath")
- } else {
- Common.showLog("FFmpeg Crop 比例裁剪失败: $outputPath ${it.failStackTrace}")
- }
- }
- }
-
-
- /**
- * 调整视频音量
- */
- fun setVideVolume(
- inputPath: String,
- outputPath: String, volumeLevel: Float
- ) {
-// val volumeLevel = 1.5 // 音量倍数(例如 1.5 表示音量加大 50%)
- val cmd = "-y -i $inputPath -filter:a volume=$volumeLevel -c:v copy $outputPath"
- Common.showLog("FFmpeg 音量 $cmd")
- FFmpegKit.executeAsync(cmd) { session ->
- val returnCode = session.returnCode
- if (ReturnCode.isSuccess(returnCode)) {
- Common.showLog("FFmpeg 音量调节成功")
- } else {
- Common.showLog("FFmpeg 音量调节失败: ${session.failStackTrace}")
- }
- }
-
- }
-
- /**
- * 调整视频播放速度
- */
- fun buildSpeedCommand(inputPath: String, outputPath: String, speed: Float) {
- val videoFilter = "setpts=${1 / speed}*PTS"
- val audioFilter = "atempo=$speed"
- val cmd = "-y -i $inputPath -filter_complex \"[0:v]$videoFilter[v];[0:a]$audioFilter[a]\" -map \"[v]\" -map \"[a]\" -preset ultrafast $outputPath"
- Common.showLog("FFmpeg 播放速度调整 $cmd")
- FFmpegKit.executeAsync(cmd) { session ->
- if (ReturnCode.isSuccess(session.returnCode)) {
- Common.showLog("FFmpeg 播放速度调整成功")
- } else {
- Common.showLog("FFmpeg 播放速度调整失败: ${session.failStackTrace}")
- }
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/logo1.png b/app/src/main/res/drawable/logo1.png
new file mode 100644
index 0000000..a0a294d
Binary files /dev/null and b/app/src/main/res/drawable/logo1.png differ
diff --git a/app/src/main/res/drawable/logo2.png b/app/src/main/res/drawable/logo2.png
new file mode 100644
index 0000000..bb4dbbc
Binary files /dev/null and b/app/src/main/res/drawable/logo2.png differ
diff --git a/app/src/main/res/drawable/test.png b/app/src/main/res/drawable/test.png
deleted file mode 100644
index f1dfb61..0000000
Binary files a/app/src/main/res/drawable/test.png and /dev/null differ
diff --git a/app/src/main/res/layout/custom_controller.xml b/app/src/main/res/layout/custom_controller.xml
index 96c7903..d9a5b54 100644
--- a/app/src/main/res/layout/custom_controller.xml
+++ b/app/src/main/res/layout/custom_controller.xml
@@ -11,8 +11,8 @@
android:layout_height="wrap_content"
android:background="@drawable/play_message_background"
android:orientation="horizontal"
- android:paddingTop="10dp"
- android:paddingBottom="10dp">
+ android:paddingTop="35dp"
+ android:paddingBottom="15dp">
+ android:src="@drawable/logo1" />
+ android:src="@drawable/logo2" />
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 099924a..4942a7b 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -9,10 +9,12 @@
\ No newline at end of file