V1.0(1)
This commit is contained in:
parent
973e0f1d30
commit
f3c2544cd4
BIN
app/RecordScreen.jks
Normal file
BIN
app/RecordScreen.jks
Normal file
Binary file not shown.
@ -1,25 +1,30 @@
|
|||||||
|
import java.util.Date
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
}
|
}
|
||||||
|
val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
|
||||||
android {
|
android {
|
||||||
namespace = "com.audio.record.screen.test"
|
namespace = "com.audio.record.screen.test"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.audio.record.screen.test"
|
applicationId = "com.audio.record.screen"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
setProperty(
|
||||||
|
"archivesBaseName",
|
||||||
|
"RecordScreen_V" + versionName + "(${versionCode})_$timestamp"
|
||||||
|
)
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = true
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
@ -79,8 +84,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||||
|
|
||||||
implementation(files("libs/jetified-ffmpeg-kit-full-6.0.aar"))
|
// 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-common-0.2.1.jar"))
|
||||||
implementation(files("libs/smart-exception-java-0.2.1.jar"))
|
// implementation(files("libs/smart-exception-java-0.2.1.jar"))
|
||||||
|
|
||||||
}
|
}
|
||||||
6
app/keystore.properties
Normal file
6
app/keystore.properties
Normal file
@ -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
|
||||||
@ -7,10 +7,12 @@
|
|||||||
android:required="false" /> <!-- 通知 -->
|
android:required="false" /> <!-- 通知 -->
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- 前台服务 -->
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- 前台服务 -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> <!-- 录音 -->
|
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 相机 -->
|
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 录音 -->
|
||||||
<uses-permission android:name="android.permission.CAMERA" /> <!-- 悬浮窗 -->
|
<uses-permission android:name="android.permission.CAMERA" /> <!-- 相机 -->
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><!-- 悬浮窗 -->
|
||||||
|
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32"
|
android:maxSdkVersion="32"
|
||||||
@ -19,6 +21,14 @@
|
|||||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32" />
|
android:maxSdkVersion="32" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- API34+ -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -32,8 +42,8 @@
|
|||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.WelcomeActivity"
|
android:name=".activity.WelcomeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:exported="true"
|
||||||
android:exported="true" >
|
android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
@ -48,28 +58,16 @@
|
|||||||
android:name=".activity.ImageViewActivity"
|
android:name=".activity.ImageViewActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
|
||||||
android:name=".activity.PreviewActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:screenOrientation="portrait" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.MainActivity1"
|
android:name=".activity.MainActivity1"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="portrait">
|
android:screenOrientation="portrait"/>
|
||||||
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".activity.MainActivity"
|
|
||||||
android:exported="false">
|
|
||||||
|
|
||||||
<!-- <intent-filter> -->
|
|
||||||
<!-- <action android:name="android.intent.action.MAIN" /> -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
|
|
||||||
<!-- </intent-filter> -->
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.ScreenPermissionActivity"
|
android:name=".activity.ScreenPermissionActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
|||||||
@ -1,286 +0,0 @@
|
|||||||
package com.audio.record.screen.test.activity
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.hardware.display.DisplayManager
|
|
||||||
import android.media.MediaRecorder
|
|
||||||
import android.media.projection.MediaProjection
|
|
||||||
import android.media.projection.MediaProjectionManager
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.audio.record.screen.test.base.BaseActivity
|
|
||||||
import com.audio.record.screen.test.databinding.ActivityMainBinding
|
|
||||||
import com.audio.record.screen.test.service.FloatingWindowBridge
|
|
||||||
import com.audio.record.screen.test.tool.Common
|
|
||||||
import com.audio.record.screen.test.tool.ScreenCaptureHelper
|
|
||||||
import com.audio.record.screen.test.tool.VideoFileHelper
|
|
||||||
|
|
||||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|
||||||
val NOTIFICATION_PERMISSION_REQUEST_CODE = 123
|
|
||||||
val SCREEN_CAPTURE_REQUEST_CODE = 124
|
|
||||||
val REQUEST_SCREENSHOT = 125
|
|
||||||
|
|
||||||
lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
|
|
||||||
lateinit var micPermissionLauncher: ActivityResultLauncher<String>
|
|
||||||
lateinit var cameraPermissionLauncher: ActivityResultLauncher<String>
|
|
||||||
lateinit var requestStoragePermission: ActivityResultLauncher<String>
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
package com.audio.record.screen.test.activity
|
package com.audio.record.screen.test.activity
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@ -13,24 +12,18 @@ import android.widget.ImageView
|
|||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.marginBottom
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.audio.record.screen.test.R
|
import com.audio.record.screen.test.R
|
||||||
import com.audio.record.screen.test.adapter.ViewPager2Adapter
|
import com.audio.record.screen.test.adapter.ViewPager2Adapter
|
||||||
import com.audio.record.screen.test.base.BaseActivity
|
import com.audio.record.screen.test.base.BaseActivity
|
||||||
import com.audio.record.screen.test.databinding.ActivityMain1Binding
|
import com.audio.record.screen.test.databinding.ActivityMain1Binding
|
||||||
import com.audio.record.screen.test.dialog.DialogPermission
|
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.ConnectionListener
|
||||||
import com.audio.record.screen.test.service.FloatingCallback
|
import com.audio.record.screen.test.service.FloatingCallback
|
||||||
import com.audio.record.screen.test.service.FloatingWindowBridge
|
import com.audio.record.screen.test.service.FloatingWindowBridge
|
||||||
import com.audio.record.screen.test.tool.Common
|
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.Permission
|
||||||
import com.audio.record.screen.test.tool.ScreenCaptureHelper
|
|
||||||
import com.audio.record.screen.test.viewmodel.MainViewModel
|
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
|
||||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
@ -64,6 +57,8 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
|
|||||||
initLauncher()
|
initLauncher()
|
||||||
firstCheck()
|
firstCheck()
|
||||||
checkStoragePermissionAndDoSomething()
|
checkStoragePermissionAndDoSomething()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initLauncher() {
|
private fun initLauncher() {
|
||||||
@ -73,6 +68,7 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
|
|||||||
if (isGranted) {
|
if (isGranted) {
|
||||||
// 权限已授予
|
// 权限已授予
|
||||||
Common.showLog("已获取存储权限")
|
Common.showLog("已获取存储权限")
|
||||||
|
|
||||||
showPermissionDialog()
|
showPermissionDialog()
|
||||||
// 执行写入文件操作
|
// 执行写入文件操作
|
||||||
} else {
|
} else {
|
||||||
@ -83,14 +79,21 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
|
|||||||
requestNotificationLauncher =
|
requestNotificationLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||||
if (isGranted) {
|
if (isGranted) {
|
||||||
startForegroundService()
|
|
||||||
Common.showLog("权限授予")
|
Common.showLog("权限授予")
|
||||||
} else {
|
} else {
|
||||||
Common.showLog("权限拒绝")
|
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(
|
// screenCaptureLauncher = registerForActivityResult(
|
||||||
// ActivityResultContracts.StartActivityForResult()
|
// ActivityResultContracts.StartActivityForResult()
|
||||||
// ) { result ->
|
// ) { result ->
|
||||||
@ -161,9 +164,10 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
|
|||||||
|
|
||||||
private fun showPermissionDialog() {
|
private fun showPermissionDialog() {
|
||||||
if (isNotification && isOverlay) {
|
if (isNotification && isOverlay) {
|
||||||
|
requestScreenPermission()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mPermissionDialog = mPermissionDialog ?: DialogPermission {
|
mPermissionDialog = mPermissionDialog ?: DialogPermission ({
|
||||||
when (it) {
|
when (it) {
|
||||||
DialogPermission.type_ball -> {
|
DialogPermission.type_ball -> {
|
||||||
intentSysWindow(this@MainActivity1)
|
intentSysWindow(this@MainActivity1)
|
||||||
@ -173,11 +177,19 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
|
|||||||
requestNotificationLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
requestNotificationLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}){
|
||||||
|
//消失
|
||||||
|
requestScreenPermission()
|
||||||
}
|
}
|
||||||
|
|
||||||
mPermissionDialog?.show(supportFragmentManager, "")
|
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) {
|
private fun intentSysWindow(context: Context) {
|
||||||
val intent = Intent(
|
val intent = Intent(
|
||||||
@ -191,9 +203,7 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
|
|||||||
private fun firstCheck() {
|
private fun firstCheck() {
|
||||||
Permission.checkNotification(this@MainActivity1) {
|
Permission.checkNotification(this@MainActivity1) {
|
||||||
isNotification = it
|
isNotification = it
|
||||||
if (it) {
|
|
||||||
startForegroundService()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Permission.checkOvalApp(this@MainActivity1) {
|
Permission.checkOvalApp(this@MainActivity1) {
|
||||||
isOverlay = it
|
isOverlay = it
|
||||||
|
|||||||
@ -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<ActivityPreviewBinding>(), View.OnClickListener {
|
|
||||||
|
|
||||||
private lateinit var exoPlayer: ExoPlayer
|
|
||||||
|
|
||||||
//将当前需要处理的原视频复制到内部存储,方便操作
|
|
||||||
private lateinit var copyFile: File
|
|
||||||
|
|
||||||
private var copyResult by Delegates.notNull<Boolean>()
|
|
||||||
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?) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -2,12 +2,18 @@ package com.audio.record.screen.test.activity
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
import android.media.projection.MediaProjectionManager
|
import android.media.projection.MediaProjectionManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.PersistableBundle
|
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.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
import com.audio.record.screen.test.base.BaseActivity
|
import com.audio.record.screen.test.base.BaseActivity
|
||||||
import com.audio.record.screen.test.databinding.ActivityPlayBinding
|
import com.audio.record.screen.test.databinding.ActivityPlayBinding
|
||||||
import com.audio.record.screen.test.service.FloatingWindowBridge
|
import com.audio.record.screen.test.service.FloatingWindowBridge
|
||||||
@ -29,14 +35,46 @@ class ScreenPermissionActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
private var withAudio = false
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
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)
|
withAudio = intent.getBooleanExtra(key_with_audio, false)
|
||||||
screenCaptureLauncher = registerForActivityResult(
|
screenCaptureLauncher = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) { result ->
|
) { result ->
|
||||||
|
Common.showLog("--------------screenCaptureLauncher")
|
||||||
if (result.resultCode == RESULT_OK && result.data != null) {
|
if (result.resultCode == RESULT_OK && result.data != null) {
|
||||||
val data: Intent? = result.data
|
val data: Intent? = result.data
|
||||||
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
|
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
|
||||||
@ -63,7 +101,7 @@ class ScreenPermissionActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
|
|
||||||
private fun requestScreenPermission() {
|
private fun requestScreenPermission() {
|
||||||
val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
Common.showLog("--------------requestScreenPermission")
|
||||||
val intent = mediaProjectionManager.createScreenCaptureIntent()
|
val intent = mediaProjectionManager.createScreenCaptureIntent()
|
||||||
screenCaptureLauncher.launch(intent)
|
screenCaptureLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.audio.record.screen.test.dialog
|
package com.audio.record.screen.test.dialog
|
||||||
|
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Bundle
|
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.audio.record.screen.test.tool.Permission
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
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
|
private lateinit var vb: DialogPermissionBinding
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
@ -28,6 +29,7 @@ class DialogPermission(private var mClickType: (type: Int) -> Unit) : BottomShee
|
|||||||
val dialog = dialog
|
val dialog = dialog
|
||||||
if (dialog != null) {
|
if (dialog != null) {
|
||||||
dialog.setCanceledOnTouchOutside(false)
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
dialog.setCancelable(false)
|
||||||
val window = dialog.window
|
val window = dialog.window
|
||||||
if (window != null) {
|
if (window != null) {
|
||||||
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
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() {
|
private fun init() {
|
||||||
vb.run {
|
vb.run {
|
||||||
|
|
||||||
|
|||||||
@ -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<FragmentCropBinding>(), 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<FragmentCutBinding>() ,View.OnClickListener{
|
|
||||||
//毫秒单位
|
|
||||||
private var leftV by Delegates.notNull<Float>()
|
|
||||||
private lateinit var viewModel: PreviewViewModel
|
|
||||||
private var rightV by Delegates.notNull<Float>()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<FragmentSpeedBinding>(), 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -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<FragmentVolumeBinding>(), 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -197,10 +197,12 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setScreenshot(boolean: Boolean) {
|
private fun setScreenshot(boolean: Boolean) {
|
||||||
|
Common.showLog("----------setScreenshot=${boolean}")
|
||||||
if (boolean) {
|
if (boolean) {
|
||||||
requestRecordPermission(ConstValue.type_show_screenshot){
|
// requestRecordPermission(ConstValue.type_show_screenshot){
|
||||||
|
//// FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
||||||
|
// }
|
||||||
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_hide_screenshot)
|
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_hide_screenshot)
|
||||||
@ -217,7 +219,7 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
|
|
||||||
private fun startRecorder() {
|
private fun startRecorder() {
|
||||||
requestRecordPermission( ConstValue.type_record){
|
requestRecordPermission( ConstValue.type_record){
|
||||||
startCountDown()
|
// startCountDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,13 +229,9 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
*/
|
*/
|
||||||
private fun requestRecordPermission(type:Int,check:(()->Unit)? = null){
|
private fun requestRecordPermission(type:Int,check:(()->Unit)? = null){
|
||||||
// TODO: requestRecordPermission
|
// TODO: requestRecordPermission
|
||||||
if (FloatingWindowBridge.getMediaProjection() == null) {
|
|
||||||
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
|
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
|
||||||
pendingType = type
|
pendingType = type
|
||||||
recorderLauncher.launch(captureIntent)
|
recorderLauncher.launch(captureIntent)
|
||||||
}else{
|
|
||||||
check?.invoke()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -108,6 +108,7 @@ class RecordingFragment : BaseFragment<FragmentRecordingBinding>(), FloatingCall
|
|||||||
|
|
||||||
override fun onUpdateRecordTime(time: String) {
|
override fun onUpdateRecordTime(time: String) {
|
||||||
super.onUpdateRecordTime(time)
|
super.onUpdateRecordTime(time)
|
||||||
|
Common.showLog("=======onUpdateRecordTime 时间更新")
|
||||||
binding.tvTimer.text = time
|
binding.tvTimer.text = time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -146,7 +146,7 @@ class ScreenRecordService : Service() {
|
|||||||
|
|
||||||
private val timeUpdateRunnable = object : Runnable {
|
private val timeUpdateRunnable = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
|
Common.showLog("=======timeUpdateRunnable 时间更新 isPause=${isPause}")
|
||||||
val currentRecordingTimeInMillis =
|
val currentRecordingTimeInMillis =
|
||||||
System.currentTimeMillis() - recordingStartTime - totalPausedTime
|
System.currentTimeMillis() - recordingStartTime - totalPausedTime
|
||||||
val elapsed = currentRecordingTimeInMillis / 1000
|
val elapsed = currentRecordingTimeInMillis / 1000
|
||||||
@ -156,9 +156,11 @@ class ScreenRecordService : Service() {
|
|||||||
callbacks.forEach {
|
callbacks.forEach {
|
||||||
it.get()?.onUpdateRecordTime(onRecordingTimeChanged)
|
it.get()?.onUpdateRecordTime(onRecordingTimeChanged)
|
||||||
}
|
}
|
||||||
if (!isPause)
|
if (!isPause){
|
||||||
mRecorderHandler.postDelayed(this, updateInterval)
|
mRecorderHandler.postDelayed(this, updateInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class FloatingBinder : Binder() {
|
inner class FloatingBinder : Binder() {
|
||||||
@ -277,8 +279,9 @@ class ScreenRecordService : Service() {
|
|||||||
*/
|
*/
|
||||||
private fun startScreenshot(showView: Boolean) {
|
private fun startScreenshot(showView: Boolean) {
|
||||||
// TODO: 开始截屏
|
// TODO: 开始截屏
|
||||||
mIntent?.let { intent ->
|
|
||||||
mediaProjectionManager.getMediaProjection(mCode, intent)?.let { mediaProjection ->
|
createMediaProject {
|
||||||
|
it?.let { mediaProjection->
|
||||||
hideScreenshot()
|
hideScreenshot()
|
||||||
val intent = Intent(this, ScreenshotAnimActivity::class.java)
|
val intent = Intent(this, ScreenshotAnimActivity::class.java)
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
@ -291,8 +294,10 @@ class ScreenRecordService : Service() {
|
|||||||
if (showView)
|
if (showView)
|
||||||
showScreenshot()
|
showScreenshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@ -384,7 +389,7 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
|
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
|
||||||
}
|
}
|
||||||
if (!isBallViewAdded&&!isBallExpandViewAdded) {
|
if (!isBallViewAdded && !isBallExpandViewAdded) {
|
||||||
windowManager.addView(ballView, layoutParamsBall)
|
windowManager.addView(ballView, layoutParamsBall)
|
||||||
isBallViewAdded = true
|
isBallViewAdded = true
|
||||||
}
|
}
|
||||||
@ -482,6 +487,7 @@ class ScreenRecordService : Service() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
layoutParamsBallExpand.y = aY
|
layoutParamsBallExpand.y = aY
|
||||||
|
updateBall()
|
||||||
windowManager.addView(ballViewExpand, layoutParamsBallExpand)
|
windowManager.addView(ballViewExpand, layoutParamsBallExpand)
|
||||||
isBallExpandViewAdded = true
|
isBallExpandViewAdded = true
|
||||||
delayRemove {
|
delayRemove {
|
||||||
@ -529,7 +535,8 @@ class ScreenRecordService : Service() {
|
|||||||
windowManager,
|
windowManager,
|
||||||
true
|
true
|
||||||
) {
|
) {
|
||||||
startScreenshot(true)
|
ballType = ConstValue.type_screenshot
|
||||||
|
intentPermission(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isScreenshotViewAdded) {
|
if (!isScreenshotViewAdded) {
|
||||||
@ -662,10 +669,6 @@ class ScreenRecordService : Service() {
|
|||||||
//倒计时结束后,开启录制
|
//倒计时结束后,开启录制
|
||||||
Common.showLog("Service--- 倒计时结束")
|
Common.showLog("Service--- 倒计时结束")
|
||||||
hideCountDown()
|
hideCountDown()
|
||||||
mIntent?.let { intent ->
|
|
||||||
mediaProjectionManager.getMediaProjection(mCode, intent)
|
|
||||||
?.let { media ->
|
|
||||||
mediaProjection = media
|
|
||||||
startRecording()
|
startRecording()
|
||||||
callbacks.forEach {
|
callbacks.forEach {
|
||||||
it.get()?.onStartRecording()
|
it.get()?.onStartRecording()
|
||||||
@ -673,10 +676,18 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
private fun animateNext(
|
||||||
@ -741,6 +752,7 @@ class ScreenRecordService : Service() {
|
|||||||
totalPausedTime = 0L
|
totalPausedTime = 0L
|
||||||
mRecorderHandler.post(timeUpdateRunnable)
|
mRecorderHandler.post(timeUpdateRunnable)
|
||||||
|
|
||||||
|
createMediaProject{ curmed->
|
||||||
mediaProjection?.let {
|
mediaProjection?.let {
|
||||||
it.registerCallback(object : MediaProjection.Callback() {
|
it.registerCallback(object : MediaProjection.Callback() {
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
@ -774,6 +786,8 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun releaseAll() {
|
private fun releaseAll() {
|
||||||
if (::mediaRecorder.isInitialized) {
|
if (::mediaRecorder.isInitialized) {
|
||||||
@ -922,7 +936,11 @@ class ScreenRecordService : Service() {
|
|||||||
|
|
||||||
fun getViewStatus() {
|
fun getViewStatus() {
|
||||||
callbacks.forEach {
|
callbacks.forEach {
|
||||||
it.get()?.onRefreshViewShow(isWebcamViewAdded, isScreenshotViewAdded, isBallViewAdded||isBallExpandViewAdded)
|
it.get()?.onRefreshViewShow(
|
||||||
|
isWebcamViewAdded,
|
||||||
|
isScreenshotViewAdded,
|
||||||
|
isBallViewAdded || isBallExpandViewAdded
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.audio.record.screen.test.tool
|
package com.audio.record.screen.test.tool
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -17,6 +18,8 @@ import android.view.KeyCharacterMap
|
|||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewConfiguration
|
import android.view.ViewConfiguration
|
||||||
|
import android.view.WindowInsets
|
||||||
|
import android.view.WindowInsetsController
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
@ -40,26 +43,6 @@ object Common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setStatusBarTextColor(activity: Activity, dark: Boolean) {
|
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 window = activity.window
|
||||||
val decor = window.decorView
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
BIN
app/src/main/res/drawable/logo1.png
Normal file
BIN
app/src/main/res/drawable/logo1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
BIN
app/src/main/res/drawable/logo2.png
Normal file
BIN
app/src/main/res/drawable/logo2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 668 KiB |
@ -11,8 +11,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/play_message_background"
|
android:background="@drawable/play_message_background"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingTop="10dp"
|
android:paddingTop="35dp"
|
||||||
android:paddingBottom="10dp">
|
android:paddingBottom="15dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/back"
|
android:id="@+id/back"
|
||||||
|
|||||||
@ -33,10 +33,10 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon_ball"
|
android:id="@+id/icon_ball"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="35dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="35dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:src="@drawable/tab1_normal" />
|
android:src="@drawable/logo1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/enable_tv_ball"
|
android:id="@+id/enable_tv_ball"
|
||||||
@ -98,10 +98,10 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon_notification"
|
android:id="@+id/icon_notification"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="35dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="35dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:src="@drawable/tab1_normal" />
|
android:src="@drawable/logo2" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/enable_tv_notification"
|
android:id="@+id/enable_tv_notification"
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
android:layout_marginEnd="25dp"
|
android:layout_marginEnd="25dp"
|
||||||
app:cardCornerRadius="12dp"
|
app:cardCornerRadius="12dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -82,10 +83,10 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_empty"
|
android:id="@+id/layout_empty"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:layout_below="@id/layout_recent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
|
|||||||
@ -9,10 +9,12 @@
|
|||||||
|
|
||||||
|
|
||||||
<style name="Theme.Transparent" parent="Theme.AppCompat.NoActionBar">
|
<style name="Theme.Transparent" parent="Theme.AppCompat.NoActionBar">
|
||||||
<item name="android:windowBackground">@color/color_transparent</item>
|
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:backgroundDimEnabled">false</item>
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
|
<item name="android:windowIsFloating">false</item>
|
||||||
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Reference in New Issue
Block a user